@sdcorejs/angular 21.1.6 → 21.2.1

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 (213) hide show
  1. package/README.md +10 -0
  2. package/assets/scss/core/menu.scss +18 -0
  3. package/assets/scss/core/utilities/_misc.scss +15 -0
  4. package/assets/scss/sd-core.scss +1 -0
  5. package/fesm2022/sdcorejs-angular-components-anchor.mjs +37 -13
  6. package/fesm2022/sdcorejs-angular-components-anchor.mjs.map +1 -1
  7. package/fesm2022/sdcorejs-angular-components-api-contract-builder.mjs +3409 -0
  8. package/fesm2022/sdcorejs-angular-components-api-contract-builder.mjs.map +1 -0
  9. package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs +2 -2
  10. package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs.map +1 -1
  11. package/fesm2022/sdcorejs-angular-components-badge.mjs +27 -2
  12. package/fesm2022/sdcorejs-angular-components-badge.mjs.map +1 -1
  13. package/fesm2022/sdcorejs-angular-components-button.mjs +2 -2
  14. package/fesm2022/sdcorejs-angular-components-button.mjs.map +1 -1
  15. package/fesm2022/sdcorejs-angular-components-ckeditor-styles.mjs +1 -1
  16. package/fesm2022/sdcorejs-angular-components-ckeditor-styles.mjs.map +1 -1
  17. package/fesm2022/sdcorejs-angular-components-code-editor.mjs +3 -3
  18. package/fesm2022/sdcorejs-angular-components-code-editor.mjs.map +1 -1
  19. package/fesm2022/sdcorejs-angular-components-data-state.mjs +3 -3
  20. package/fesm2022/sdcorejs-angular-components-data-state.mjs.map +1 -1
  21. package/fesm2022/sdcorejs-angular-components-editor.mjs +2 -2
  22. package/fesm2022/sdcorejs-angular-components-editor.mjs.map +1 -1
  23. package/fesm2022/sdcorejs-angular-components-form-generic.mjs +324 -192
  24. package/fesm2022/sdcorejs-angular-components-form-generic.mjs.map +1 -1
  25. package/fesm2022/sdcorejs-angular-components-history.mjs +4 -4
  26. package/fesm2022/sdcorejs-angular-components-history.mjs.map +1 -1
  27. package/fesm2022/sdcorejs-angular-components-import-excel.mjs +13 -5
  28. package/fesm2022/sdcorejs-angular-components-import-excel.mjs.map +1 -1
  29. package/fesm2022/sdcorejs-angular-components-inform.mjs +3 -3
  30. package/fesm2022/sdcorejs-angular-components-inform.mjs.map +1 -1
  31. package/fesm2022/sdcorejs-angular-components-modal-resizable.mjs +164 -8
  32. package/fesm2022/sdcorejs-angular-components-modal-resizable.mjs.map +1 -1
  33. package/fesm2022/sdcorejs-angular-components-modal.mjs +30 -18
  34. package/fesm2022/sdcorejs-angular-components-modal.mjs.map +1 -1
  35. package/fesm2022/sdcorejs-angular-components-operator.mjs +2 -2
  36. package/fesm2022/sdcorejs-angular-components-operator.mjs.map +1 -1
  37. package/fesm2022/sdcorejs-angular-components-org-chart.mjs +60 -13
  38. package/fesm2022/sdcorejs-angular-components-org-chart.mjs.map +1 -1
  39. package/fesm2022/sdcorejs-angular-components-preview.mjs +27 -17
  40. package/fesm2022/sdcorejs-angular-components-preview.mjs.map +1 -1
  41. package/fesm2022/sdcorejs-angular-components-query-bar.mjs +192 -30
  42. package/fesm2022/sdcorejs-angular-components-query-bar.mjs.map +1 -1
  43. package/fesm2022/sdcorejs-angular-components-query-builder.mjs +187 -127
  44. package/fesm2022/sdcorejs-angular-components-query-builder.mjs.map +1 -1
  45. package/fesm2022/sdcorejs-angular-components-section.mjs +33 -2
  46. package/fesm2022/sdcorejs-angular-components-section.mjs.map +1 -1
  47. package/fesm2022/sdcorejs-angular-components-side-drawer.mjs +83 -24
  48. package/fesm2022/sdcorejs-angular-components-side-drawer.mjs.map +1 -1
  49. package/fesm2022/sdcorejs-angular-components-splitter.mjs +57 -21
  50. package/fesm2022/sdcorejs-angular-components-splitter.mjs.map +1 -1
  51. package/fesm2022/sdcorejs-angular-components-stepper.mjs +2 -2
  52. package/fesm2022/sdcorejs-angular-components-stepper.mjs.map +1 -1
  53. package/fesm2022/sdcorejs-angular-components-tab-router.mjs +68 -20
  54. package/fesm2022/sdcorejs-angular-components-tab-router.mjs.map +1 -1
  55. package/fesm2022/sdcorejs-angular-components-tab.mjs +3 -2
  56. package/fesm2022/sdcorejs-angular-components-tab.mjs.map +1 -1
  57. package/fesm2022/sdcorejs-angular-components-table.mjs +556 -268
  58. package/fesm2022/sdcorejs-angular-components-table.mjs.map +1 -1
  59. package/fesm2022/sdcorejs-angular-components-tree.mjs +80 -17
  60. package/fesm2022/sdcorejs-angular-components-tree.mjs.map +1 -1
  61. package/fesm2022/sdcorejs-angular-components-upload-file.mjs +48 -13
  62. package/fesm2022/sdcorejs-angular-components-upload-file.mjs.map +1 -1
  63. package/fesm2022/sdcorejs-angular-components.mjs +7 -1
  64. package/fesm2022/sdcorejs-angular-components.mjs.map +1 -1
  65. package/fesm2022/sdcorejs-angular-configurations.mjs.map +1 -1
  66. package/fesm2022/sdcorejs-angular-directives.mjs +45 -8
  67. package/fesm2022/sdcorejs-angular-directives.mjs.map +1 -1
  68. package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs +40 -29
  69. package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs.map +1 -1
  70. package/fesm2022/sdcorejs-angular-forms-checkbox.mjs +5 -5
  71. package/fesm2022/sdcorejs-angular-forms-checkbox.mjs.map +1 -1
  72. package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs +67 -50
  73. package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs.map +1 -1
  74. package/fesm2022/sdcorejs-angular-forms-chip.mjs +57 -43
  75. package/fesm2022/sdcorejs-angular-forms-chip.mjs.map +1 -1
  76. package/fesm2022/sdcorejs-angular-forms-date-range.mjs +232 -61
  77. package/fesm2022/sdcorejs-angular-forms-date-range.mjs.map +1 -1
  78. package/fesm2022/sdcorejs-angular-forms-date.mjs +96 -24
  79. package/fesm2022/sdcorejs-angular-forms-date.mjs.map +1 -1
  80. package/fesm2022/sdcorejs-angular-forms-datetime.mjs +131 -27
  81. package/fesm2022/sdcorejs-angular-forms-datetime.mjs.map +1 -1
  82. package/fesm2022/sdcorejs-angular-forms-entity-picker.mjs +40 -16
  83. package/fesm2022/sdcorejs-angular-forms-entity-picker.mjs.map +1 -1
  84. package/fesm2022/sdcorejs-angular-forms-input-color.mjs +16 -5
  85. package/fesm2022/sdcorejs-angular-forms-input-color.mjs.map +1 -1
  86. package/fesm2022/sdcorejs-angular-forms-input-number.mjs +24 -9
  87. package/fesm2022/sdcorejs-angular-forms-input-number.mjs.map +1 -1
  88. package/fesm2022/sdcorejs-angular-forms-input.mjs +58 -10
  89. package/fesm2022/sdcorejs-angular-forms-input.mjs.map +1 -1
  90. package/fesm2022/sdcorejs-angular-forms-models.mjs +227 -6
  91. package/fesm2022/sdcorejs-angular-forms-models.mjs.map +1 -1
  92. package/fesm2022/sdcorejs-angular-forms-radio.mjs +4 -4
  93. package/fesm2022/sdcorejs-angular-forms-radio.mjs.map +1 -1
  94. package/fesm2022/sdcorejs-angular-forms-select.mjs +136 -38
  95. package/fesm2022/sdcorejs-angular-forms-select.mjs.map +1 -1
  96. package/fesm2022/sdcorejs-angular-forms-switch.mjs +18 -3
  97. package/fesm2022/sdcorejs-angular-forms-switch.mjs.map +1 -1
  98. package/fesm2022/sdcorejs-angular-forms-textarea.mjs +51 -62
  99. package/fesm2022/sdcorejs-angular-forms-textarea.mjs.map +1 -1
  100. package/fesm2022/sdcorejs-angular-forms-time-range.mjs +57 -12
  101. package/fesm2022/sdcorejs-angular-forms-time-range.mjs.map +1 -1
  102. package/fesm2022/sdcorejs-angular-forms-time.mjs +16 -5
  103. package/fesm2022/sdcorejs-angular-forms-time.mjs.map +1 -1
  104. package/fesm2022/sdcorejs-angular-forms-tree-select.mjs +100 -9
  105. package/fesm2022/sdcorejs-angular-forms-tree-select.mjs.map +1 -1
  106. package/fesm2022/sdcorejs-angular-forms.mjs +91 -46
  107. package/fesm2022/sdcorejs-angular-forms.mjs.map +1 -1
  108. package/fesm2022/sdcorejs-angular-handlers.mjs +18 -3
  109. package/fesm2022/sdcorejs-angular-handlers.mjs.map +1 -1
  110. package/fesm2022/sdcorejs-angular-i18n.mjs +895 -68
  111. package/fesm2022/sdcorejs-angular-i18n.mjs.map +1 -1
  112. package/fesm2022/sdcorejs-angular-interceptors.mjs +87 -29
  113. package/fesm2022/sdcorejs-angular-interceptors.mjs.map +1 -1
  114. package/fesm2022/sdcorejs-angular-modules-auth.mjs +25 -15
  115. package/fesm2022/sdcorejs-angular-modules-auth.mjs.map +1 -1
  116. package/fesm2022/sdcorejs-angular-modules-icon.mjs +2 -2
  117. package/fesm2022/sdcorejs-angular-modules-icon.mjs.map +1 -1
  118. package/fesm2022/sdcorejs-angular-modules-keycloak.mjs +32 -3
  119. package/fesm2022/sdcorejs-angular-modules-keycloak.mjs.map +1 -1
  120. package/fesm2022/sdcorejs-angular-modules-layout.mjs +342 -111
  121. package/fesm2022/sdcorejs-angular-modules-layout.mjs.map +1 -1
  122. package/fesm2022/sdcorejs-angular-modules-permission.mjs +177 -40
  123. package/fesm2022/sdcorejs-angular-modules-permission.mjs.map +1 -1
  124. package/fesm2022/sdcorejs-angular-pipes.mjs +62 -28
  125. package/fesm2022/sdcorejs-angular-pipes.mjs.map +1 -1
  126. package/fesm2022/sdcorejs-angular-services-api.mjs +123 -13
  127. package/fesm2022/sdcorejs-angular-services-api.mjs.map +1 -1
  128. package/fesm2022/sdcorejs-angular-services-cache.mjs +53 -3
  129. package/fesm2022/sdcorejs-angular-services-cache.mjs.map +1 -1
  130. package/fesm2022/sdcorejs-angular-services-confirm.mjs +25 -73
  131. package/fesm2022/sdcorejs-angular-services-confirm.mjs.map +1 -1
  132. package/fesm2022/sdcorejs-angular-services-excel.mjs +12 -10
  133. package/fesm2022/sdcorejs-angular-services-excel.mjs.map +1 -1
  134. package/fesm2022/sdcorejs-angular-services-loading.mjs +75 -84
  135. package/fesm2022/sdcorejs-angular-services-loading.mjs.map +1 -1
  136. package/fesm2022/sdcorejs-angular-services-notify.mjs +46 -5
  137. package/fesm2022/sdcorejs-angular-services-notify.mjs.map +1 -1
  138. package/fesm2022/sdcorejs-angular-services-storage.mjs +4 -0
  139. package/fesm2022/sdcorejs-angular-services-storage.mjs.map +1 -1
  140. package/fesm2022/sdcorejs-angular-services.mjs +0 -1
  141. package/fesm2022/sdcorejs-angular-services.mjs.map +1 -1
  142. package/fesm2022/sdcorejs-angular-utilities-extensions.mjs +225 -80
  143. package/fesm2022/sdcorejs-angular-utilities-extensions.mjs.map +1 -1
  144. package/fesm2022/sdcorejs-angular-utilities-models.mjs +1 -63
  145. package/fesm2022/sdcorejs-angular-utilities-models.mjs.map +1 -1
  146. package/fesm2022/sdcorejs-angular.mjs +17 -10
  147. package/fesm2022/sdcorejs-angular.mjs.map +1 -1
  148. package/package.json +36 -25
  149. package/types/sdcorejs-angular-components-anchor.d.ts +3 -1
  150. package/types/sdcorejs-angular-components-api-contract-builder.d.ts +694 -0
  151. package/types/sdcorejs-angular-components-badge.d.ts +2 -0
  152. package/types/sdcorejs-angular-components-ckeditor-styles.d.ts +1 -1
  153. package/types/sdcorejs-angular-components-form-generic.d.ts +44 -33
  154. package/types/sdcorejs-angular-components-import-excel.d.ts +1 -0
  155. package/types/sdcorejs-angular-components-mini-editor.d.ts +1 -1
  156. package/types/sdcorejs-angular-components-modal-resizable.d.ts +92 -0
  157. package/types/sdcorejs-angular-components-modal.d.ts +3 -2
  158. package/types/sdcorejs-angular-components-org-chart.d.ts +6 -0
  159. package/types/sdcorejs-angular-components-preview.d.ts +1 -0
  160. package/types/sdcorejs-angular-components-query-bar.d.ts +55 -17
  161. package/types/sdcorejs-angular-components-query-builder.d.ts +150 -98
  162. package/types/sdcorejs-angular-components-section.d.ts +5 -0
  163. package/types/sdcorejs-angular-components-tab-router.d.ts +19 -19
  164. package/types/sdcorejs-angular-components-table.d.ts +251 -109
  165. package/types/sdcorejs-angular-components-tree.d.ts +48 -30
  166. package/types/sdcorejs-angular-components-upload-file.d.ts +6 -3
  167. package/types/sdcorejs-angular-components.d.ts +7 -1
  168. package/types/sdcorejs-angular-configurations.d.ts +1 -2
  169. package/types/sdcorejs-angular-forms-autocomplete.d.ts +5 -2
  170. package/types/sdcorejs-angular-forms-chip-calendar.d.ts +10 -0
  171. package/types/sdcorejs-angular-forms-date-range.d.ts +14 -2
  172. package/types/sdcorejs-angular-forms-date.d.ts +13 -2
  173. package/types/sdcorejs-angular-forms-datetime.d.ts +13 -2
  174. package/types/sdcorejs-angular-forms-entity-picker.d.ts +9 -3
  175. package/types/sdcorejs-angular-forms-input-color.d.ts +4 -0
  176. package/types/sdcorejs-angular-forms-input-number.d.ts +3 -0
  177. package/types/sdcorejs-angular-forms-input.d.ts +3 -0
  178. package/types/sdcorejs-angular-forms-models.d.ts +119 -4
  179. package/types/sdcorejs-angular-forms-select.d.ts +27 -4
  180. package/types/sdcorejs-angular-forms-switch.d.ts +7 -1
  181. package/types/sdcorejs-angular-forms-textarea.d.ts +5 -3
  182. package/types/sdcorejs-angular-forms-time-range.d.ts +22 -1
  183. package/types/sdcorejs-angular-forms-tree-select.d.ts +36 -5
  184. package/types/sdcorejs-angular-forms.d.ts +22 -13
  185. package/types/sdcorejs-angular-i18n.d.ts +175 -16
  186. package/types/sdcorejs-angular-interceptors.d.ts +13 -3
  187. package/types/sdcorejs-angular-modules-auth.d.ts +1 -1
  188. package/types/sdcorejs-angular-modules-keycloak.d.ts +1 -0
  189. package/types/sdcorejs-angular-modules-layout.d.ts +94 -28
  190. package/types/sdcorejs-angular-modules-permission.d.ts +66 -4
  191. package/types/sdcorejs-angular-pipes.d.ts +27 -5
  192. package/types/sdcorejs-angular-services-api.d.ts +21 -1
  193. package/types/sdcorejs-angular-services-cache.d.ts +11 -1
  194. package/types/sdcorejs-angular-services-notify.d.ts +1 -1
  195. package/types/sdcorejs-angular-services.d.ts +0 -1
  196. package/types/sdcorejs-angular-utilities-extensions.d.ts +83 -61
  197. package/types/sdcorejs-angular-utilities-models.d.ts +2 -76
  198. package/types/sdcorejs-angular.d.ts +11 -2
  199. package/fesm2022/sdcorejs-angular-components-base.mjs +0 -23
  200. package/fesm2022/sdcorejs-angular-components-base.mjs.map +0 -1
  201. package/fesm2022/sdcorejs-angular-components-document-builder.mjs +0 -5038
  202. package/fesm2022/sdcorejs-angular-components-document-builder.mjs.map +0 -1
  203. package/fesm2022/sdcorejs-angular-models.mjs +0 -6
  204. package/fesm2022/sdcorejs-angular-models.mjs.map +0 -1
  205. package/fesm2022/sdcorejs-angular-services-docx.mjs +0 -291
  206. package/fesm2022/sdcorejs-angular-services-docx.mjs.map +0 -1
  207. package/fesm2022/sdcorejs-angular-services-license.mjs +0 -97
  208. package/fesm2022/sdcorejs-angular-services-license.mjs.map +0 -1
  209. package/types/sdcorejs-angular-components-base.d.ts +0 -11
  210. package/types/sdcorejs-angular-components-document-builder.d.ts +0 -252
  211. package/types/sdcorejs-angular-models.d.ts +0 -2
  212. package/types/sdcorejs-angular-services-docx.d.ts +0 -27
  213. package/types/sdcorejs-angular-services-license.d.ts +0 -12
@@ -4,7 +4,7 @@ import * as i0 from '@angular/core';
4
4
  import { viewChild, contentChild, inject, ChangeDetectorRef, input, computed, booleanAttribute, model, output, EventEmitter, signal, effect, untracked, Output, ChangeDetectionStrategy, Component } from '@angular/core';
5
5
  import { toObservable } from '@angular/core/rxjs-interop';
6
6
  import * as i2 from '@angular/forms';
7
- import { NgForm, FormGroup, Validators, FormsModule, ReactiveFormsModule } from '@angular/forms';
7
+ import { NgForm, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms';
8
8
  import * as i5 from '@angular/material/autocomplete';
9
9
  import { MatAutocompleteTrigger, MatAutocompleteModule } from '@angular/material/autocomplete';
10
10
  import { MatFormFieldModule } from '@angular/material/form-field';
@@ -17,11 +17,10 @@ import { MatTooltipModule } from '@angular/material/tooltip';
17
17
  import { SdView } from '@sdcorejs/angular/components/view';
18
18
  import { SdItemDefDefDirective, SdViewDefDirective } from '@sdcorejs/angular/forms/directives';
19
19
  import { SdLabel } from '@sdcorejs/angular/forms/label';
20
- import { SD_FORM_CONFIGURATION, sdFormControlState, sdViewedTransform, sdViewedInline, SdFormControl, ɵsdFormControlConnector as _sdFormControlConnector, HandleSdCustomValidator, SdInlineErrorValidator } from '@sdcorejs/angular/forms/models';
20
+ import { SD_FORM_CONFIGURATION, ɵsdTimerScope as _sdTimerScope, sdFormControlState, sdViewedTransform, sdViewedInline, SdFormControl, ɵsdFormControlConnector as _sdFormControlConnector, HandleSdCustomValidator, SdInlineErrorValidator } from '@sdcorejs/angular/forms/models';
21
21
  import { I18nService } from '@sdcorejs/angular/i18n';
22
22
  import { sdIsEmpty, sdSerializeDataValue } from '@sdcorejs/angular/utilities/data-state';
23
- import { ArrayUtilities } from '@sdcorejs/angular/utilities/extensions';
24
- import { Utilities } from '@sdcorejs/utils/fns';
23
+ import { Utilities, ArrayUtilities } from '@sdcorejs/utils/fns';
25
24
  import { Subscription, combineLatest, timer, of, defer, from } from 'rxjs';
26
25
  import { tap, map, startWith, debounce, switchMap, catchError } from 'rxjs/operators';
27
26
  import { SdIcon } from '@sdcorejs/angular/modules/icon';
@@ -38,6 +37,9 @@ class SdAutocompleteErrotStateMatcher {
38
37
  }
39
38
  class SdAutocomplete {
40
39
  id = `I${Utilities.generateUuid()}`;
40
+ /** why: id ổn định của <mat-error> để control trỏ `aria-describedby` sang — thông báo lỗi
41
+ * phải đọc được từ chính control, không chỉ hiện ra màn hình. */
42
+ errorId = `${this.id}-error`;
41
43
  // ==========================================
42
44
  // 1. SIGNAL QUERIES
43
45
  // ==========================================
@@ -53,6 +55,9 @@ class SdAutocomplete {
53
55
  ref = inject(ChangeDetectorRef);
54
56
  formConfig = inject(SD_FORM_CONFIGURATION, { optional: true });
55
57
  #i18n = inject(I18nService);
58
+ // why: focus() (và open() gọi lại nó) hoãn 100ms rồi mới openPanel(). Không giữ handle thì
59
+ // panel overlay có thể mở SAU khi control đã destroy và không còn ai đóng nó.
60
+ #timers = _sdTimerScope();
56
61
  // ==========================================
57
62
  // 3. SIGNAL INPUTS & MODEL
58
63
  // ==========================================
@@ -153,10 +158,21 @@ class SdAutocomplete {
153
158
  isTyping = signal(false, ...(ngDevMode ? [{ debugName: "isTyping" }] : /* istanbul ignore next */ []));
154
159
  inputControl = new SdFormControl();
155
160
  formControl = new SdFormControl();
161
+ // why: validator đi qua connector (addValidators/removeValidators — additive) thay vì
162
+ // setValidators()/setAsyncValidators() như trước. `formControl` là public API, consumer hoàn toàn
163
+ // có thể tự gắn validator lên nó; setValidators THAY THẾ cả danh sách nên xoá sạch validator của
164
+ // consumer mỗi lần required/[validator]/inlineError đổi. Connector chỉ thêm/gỡ đúng phần component
165
+ // sở hữu, phần còn lại của danh sách giữ nguyên.
156
166
  #formConnector = _sdFormControlConnector({
157
167
  form: this.form,
158
168
  name: this.name,
159
169
  control: computed(() => this.formControl),
170
+ required: this.required,
171
+ validators: computed(() => (this.inlineError() ? [SdInlineErrorValidator] : null)),
172
+ asyncValidators: computed(() => {
173
+ const custom = this.validator();
174
+ return custom ? [HandleSdCustomValidator(custom)] : null;
175
+ }),
160
176
  });
161
177
  matcher = new SdAutocompleteErrotStateMatcher(this.formControl);
162
178
  #cache = {};
@@ -205,12 +221,6 @@ class SdAutocomplete {
205
221
  this.formControl.enable({ emitEvent: false });
206
222
  }
207
223
  });
208
- effect(() => {
209
- const req = this.required();
210
- const val = this.validator();
211
- const inl = this.inlineError();
212
- untracked(() => this.#updateValidator(req, val, inl));
213
- });
214
224
  }
215
225
  ngOnInit() {
216
226
  this.#subscription.add(this.formControl.valueChanges.subscribe(val => {
@@ -399,7 +409,8 @@ class SdAutocomplete {
399
409
  };
400
410
  focus = () => {
401
411
  this.isFocused = true;
402
- setTimeout(() => {
412
+ // why: vẫn 100ms như cũ — chỉ scope handle theo DestroyRef.
413
+ this.#timers.schedule(() => {
403
414
  this.autocompleteTrigger()?.openPanel();
404
415
  this.inputRef()?.nativeElement?.focus();
405
416
  }, 100);
@@ -414,8 +425,18 @@ class SdAutocomplete {
414
425
  $event?.stopPropagation();
415
426
  this.filteredItems.set([]);
416
427
  this.inputControl?.setValue('');
417
- if (this.valueModel()) {
418
- this.formControl.setValue(null, { emitEvent: false });
428
+ // why: guard bằng `!= null && !== ''` chứ KHÔNG dùng truthiness. Giá trị `0` là hợp lệ —
429
+ // load path (selected$) đã coi `val === 0` là có giá trị — nên `if (this.valueModel())`
430
+ // im lặng từ chối xoá đúng những item có value = 0 (hoặc false).
431
+ const current = this.valueModel();
432
+ if (current != null && current !== '') {
433
+ // why: KHÔNG dùng { emitEvent: false } khi reset formControl. formControl mang async
434
+ // [validator] (HandleSdCustomValidator). Chặn event thì lúc async resolve, setErrors cũng im →
435
+ // #state (sdFormControlState) không tick → errorMessage không recompute → message lỗi không
436
+ // hiện/không clear sau khi xoá. Đúng lỗi mà comment ở onSelect nói là đã fix. Subscriber
437
+ // formControl.valueChanges set valueModel có guard `!==`, và clear() set lại cùng giá trị
438
+ // (no-op) → không lặp.
439
+ this.formControl.setValue(null);
419
440
  this.valueModel.set(null);
420
441
  this.sdChange.emit(null);
421
442
  this.sdSelection.emit({ values: [null], selectedItems: [], value: null, selectedItem: null });
@@ -427,23 +448,13 @@ class SdAutocomplete {
427
448
  this.sdAdd.emit();
428
449
  };
429
450
  reValidate = () => {
430
- this.inputControl.updateValueAndValidity({ emitEvent: true });
431
- };
432
- #updateValidator = (req, val, inl) => {
433
- const validators = [];
434
- const asyncValidators = [];
435
- if (req)
436
- validators.push(Validators.required);
437
- if (val)
438
- asyncValidators.push(HandleSdCustomValidator(val));
439
- if (inl)
440
- validators.push(SdInlineErrorValidator);
441
- this.formControl.setValidators(validators.length ? validators : null);
442
- this.formControl.setAsyncValidators(asyncValidators.length ? asyncValidators : null);
443
- this.formControl.updateValueAndValidity({ emitEvent: false });
451
+ // why: mọi validator (required / [validator] / inlineError) được cài trên formControl —
452
+ // inputControl chỉ giữ text search và KHÔNG có validator nào. Gọi trên inputControl là no-op,
453
+ // tức API public reValidate() trước đây không validate gì cả.
454
+ this.formControl.updateValueAndValidity({ emitEvent: true });
444
455
  };
445
456
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdAutocomplete, deps: [], target: i0.ɵɵFactoryTarget.Component });
446
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SdAutocomplete, isStandalone: true, selector: "sd-autocomplete", inputs: { autoIdInput: { classPropertyName: "autoIdInput", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, helperText: { classPropertyName: "helperText", publicName: "helperText", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, valueField: { classPropertyName: "valueField", publicName: "valueField", isSignal: true, isRequired: false, transformFunction: null }, displayField: { classPropertyName: "displayField", publicName: "displayField", isSignal: true, isRequired: false, transformFunction: null }, disabledField: { classPropertyName: "disabledField", publicName: "disabledField", isSignal: true, isRequired: false, transformFunction: null }, limit: { classPropertyName: "limit", publicName: "limit", isSignal: true, isRequired: false, transformFunction: null }, cacheChecksum: { classPropertyName: "cacheChecksum", publicName: "cacheChecksum", isSignal: true, isRequired: false, transformFunction: null }, hyperlink: { classPropertyName: "hyperlink", publicName: "hyperlink", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, hideInlineError: { classPropertyName: "hideInlineError", publicName: "hideInlineError", isSignal: true, isRequired: false, transformFunction: null }, addable: { classPropertyName: "addable", publicName: "addable", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, viewed: { classPropertyName: "viewed", publicName: "viewed", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, validator: { classPropertyName: "validator", publicName: "validator", isSignal: true, isRequired: false, transformFunction: null }, inlineError: { classPropertyName: "inlineError", publicName: "inlineError", isSignal: true, isRequired: false, transformFunction: null }, appearanceInput: { classPropertyName: "appearanceInput", publicName: "appearance", isSignal: true, isRequired: false, transformFunction: null }, valueModel: { classPropertyName: "valueModel", publicName: "model", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueModel: "modelChange", sdChange: "sdChange", sdSelection: "sdSelection", sdAdd: "sdAdd" }, host: { properties: { "class.sd-has-label": "!!label()", "class.sd-viewed": "isViewed() || isInline()", "class.sd-bare": "isInline()" } }, queries: [{ propertyName: "sdLabelTemplate", first: true, predicate: ["sdLabel"], descendants: true, isSignal: true }, { propertyName: "sdValueTemplate", first: true, predicate: ["sdValue"], descendants: true, isSignal: true }, { propertyName: "itemDef", first: true, predicate: SdItemDefDefDirective, descendants: true, isSignal: true }, { propertyName: "sdViewDef", first: true, predicate: SdViewDefDirective, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["input"], descendants: true, isSignal: true }, { propertyName: "autocompleteTrigger", first: true, predicate: MatAutocompleteTrigger, descendants: true, isSignal: true }], ngImport: i0, template: "@let _label = label();\n@let _appearance = appearance();\n@let _hideInlineError = hideInlineError();\n@let _errorMessage = errorMessage();\n@let _helperText = helperText();\n@let _required = required();\n@let _displayField = displayField();\n@let _disabledField = disabledField();\n@let iDefTpl = itemDef()?.templateRef;\n@let _loading = loading();\n@let nVal = $any(normalizedValue());\n@let _isInline = isInline();\n@let _bare = _isInline;\n@let _hasValue = nVal !== undefined && nVal !== null && nVal !== '';\n\n@if (isViewed()) {\n <sd-view\n [label]=\"_label\"\n [labelTemplate]=\"sdLabelTemplate()\"\n [value]=\"nVal\"\n [display]=\"display()\"\n [hyperlink]=\"hyperlink()\"\n [valueTemplate]=\"viewTemplate()\">\n </sd-view>\n} @else {\n @if (_isInline) {\n <!-- why: inline \u2014 sd-view l\u00E0 \"m\u1EB7t\" + trigger; autocomplete editor render \u1EA9n (.sd-inline-editor) ch\u1EC9 \u0111\u1EC3 m\u1EDF panel. -->\n <span class=\"sd-inline-view\" role=\"button\" tabindex=\"0\" (click)=\"enterInlineEdit()\" (keydown.enter)=\"enterInlineEdit()\">\n <sd-view\n [label]=\"_label\"\n [labelTemplate]=\"sdLabelTemplate()\"\n [value]=\"nVal\"\n [display]=\"display()\"\n [hyperlink]=\"hyperlink()\"\n [valueTemplate]=\"viewTemplate()\">\n </sd-view>\n @if (clearable() && _hasValue && !_required && !formControl.disabled) {\n <button type=\"button\" class=\"sd-inline-clear\" (click)=\"clear($event)\" title=\"X\u00F3a\" aria-label=\"X\u00F3a\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n }\n </span>\n }\n\n @if (!_appearance) {\n <ng-content select=\"[sdLabel]\">\n @if (_label) {\n <sd-label [label]=\"_label\" [required]=\"_required\"></sd-label>\n }\n </ng-content>\n }\n\n <div\n class=\"d-flex align-items-center\"\n [class.sd-inline-editor]=\"_isInline\"\n [class.c-focused]=\"isFocused\"\n [class.c-disabled]=\"formControl.disabled\"\n (click)=\"onClick()\"\n aria-hidden=\"true\">\n <mat-form-field\n [class.sd-md]=\"size() === 'md'\"\n [class.sd-sm]=\"size() === 'sm'\"\n [class.hide-inline-error]=\"_hideInlineError\"\n [floatLabel]=\"nVal ? 'always' : 'auto'\"\n [appearance]=\"_appearance || 'outline'\">\n @if (_appearance && _label) {\n <mat-label class=\"sd-form-field-label\">\n <span>{{ _label }}</span>\n @if (_helperText) {\n <sd-icon [matTooltip]=\"_helperText\" matTooltipPosition=\"below\" name=\"info\"></sd-icon>\n }\n </mat-label>\n }\n\n <input\n [id]=\"id\"\n [formControl]=\"inputControl\"\n [placeholder]=\"controlPlaceHolder() || ''\"\n [class.c-selected]=\"nVal\"\n [matAutocomplete]=\"auto\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n matInput\n [autocomplete]=\"id\"\n autocorrect=\"off\"\n [errorStateMatcher]=\"matcher\"\n [required]=\"_required\"\n #input\n #autocompleteTrigger=\"matAutocompleteTrigger\"\n [attr.data-autoId]=\"autoId()\"\n [attr.data-disabled]=\"dataDisabled()\"\n [attr.data-invalid]=\"dataInvalid()\"\n [attr.data-empty]=\"dataEmpty()\"\n [attr.data-value]=\"dataValue()\"\n [attr.data-loading]=\"dataLoading()\"\n [attr.data-required]=\"dataRequired()\"\n [attr.data-error-message]=\"dataErrorMessage()\"\n aria-hidden=\"true\" />\n\n @if (!_loading && nVal && !inputControl.disabled && !_bare) {\n <button type=\"button\" class=\"sd-clear-btn\" matSuffix (click)=\"clear($event)\" title=\"X\u00F3a\" aria-label=\"X\u00F3a\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n } @else if (_loading) {\n <mat-spinner [diameter]=\"20\" matSuffix></mat-spinner>\n } @else {\n <sd-icon class=\"cursor-pointer sd-suffix-icon\" matSuffix name=\"search\"></sd-icon>\n }\n\n <mat-autocomplete #auto=\"matAutocomplete\" (optionSelected)=\"onSelect($event.option.value)\" class=\"sd-autocomplete-panel\">\n @let _filteredItems = filteredItems();\n\n @if (_filteredItems?.length) {\n @for (item of _filteredItems; track $index) {\n <mat-option\n [value]=\"item\"\n matTooltipPosition=\"above\"\n [matTooltip]=\"_displayField ? getNestedValue(item, _displayField) : item\"\n [disabled]=\"_disabledField ? getNestedValue(item, _disabledField) : false\">\n @if (iDefTpl) {\n <ng-container *ngTemplateOutlet=\"iDefTpl ?? null; context: { item: item }\"> </ng-container>\n } @else {\n {{ _displayField ? getNestedValue(item, _displayField) : item }}\n }\n </mat-option>\n }\n } @else if (!_filteredItems?.length && inputControl.value && !isTyping() && !_loading) {}\n\n @if (addable() && sdAdd.observed) {\n <mat-option class=\"sd__option--add\" (keyup.Space)=\"$event.stopPropagation()\" disabled=\"true\">\n <div (click)=\"onAdd($event)\">\n <sd-icon class=\"mr-1\" name=\"add\"></sd-icon>\n {{ 'New item' }}\n </div>\n </mat-option>\n }\n </mat-autocomplete>\n\n @if (_hideInlineError && _errorMessage && formControl.touched) {\n <sd-icon matSuffix class=\"sd-error-icon\" [matTooltip]=\"_errorMessage\" matTooltipPosition=\"above\" name=\"error\"></sd-icon>\n }\n\n @if (!_hideInlineError && _errorMessage && formControl.touched) {\n <mat-error>{{ _errorMessage }}</mat-error>\n }\n </mat-form-field>\n </div>\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-primary-contrast{color:var(--sd-primary-contrast)!important}.bg-primary-contrast{background:var(--sd-primary-contrast)!important}.border-primary-contrast{border-color:var(--sd-primary-contrast)!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-secondary-contrast{color:var(--sd-secondary-contrast)!important}.bg-secondary-contrast{background:var(--sd-secondary-contrast)!important}.border-secondary-contrast{border-color:var(--sd-secondary-contrast)!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-info-contrast{color:var(--sd-info-contrast)!important}.bg-info-contrast{background:var(--sd-info-contrast)!important}.border-info-contrast{border-color:var(--sd-info-contrast)!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-success-contrast{color:var(--sd-success-contrast)!important}.bg-success-contrast{background:var(--sd-success-contrast)!important}.border-success-contrast{border-color:var(--sd-success-contrast)!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-warning-contrast{color:var(--sd-warning-contrast)!important}.bg-warning-contrast{background:var(--sd-warning-contrast)!important}.border-warning-contrast{border-color:var(--sd-warning-contrast)!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-error-contrast{color:var(--sd-error-contrast)!important}.bg-error-contrast{background:var(--sd-error-contrast)!important}.border-error-contrast{border-color:var(--sd-error-contrast)!important}.text-surface{color:var(--sd-surface)!important}.bg-surface{background:var(--sd-surface)!important}.border-surface{border-color:var(--sd-surface)!important}.text-surface-muted{color:var(--sd-surface-muted)!important}.bg-surface-muted{background:var(--sd-surface-muted)!important}.border-surface-muted{border-color:var(--sd-surface-muted)!important}.text-text{color:var(--sd-text)!important}.bg-text{background:var(--sd-text)!important}.border-text{border-color:var(--sd-text)!important}.text-text-secondary{color:var(--sd-text-secondary)!important}.bg-text-secondary{background:var(--sd-text-secondary)!important}.border-text-secondary{border-color:var(--sd-text-secondary)!important}.text-text-muted{color:var(--sd-text-muted)!important}.bg-text-muted{background:var(--sd-text-muted)!important}.border-text-muted{border-color:var(--sd-text-muted)!important}.text-border{color:var(--sd-border)!important}.bg-border{background:var(--sd-border)!important}.border-border{border-color:var(--sd-border)!important}.text-border-strong{color:var(--sd-border-strong)!important}.bg-border-strong{background:var(--sd-border-strong)!important}.border-border-strong{border-color:var(--sd-border-strong)!important}.text-disabled-bg{color:var(--sd-disabled-bg)!important}.bg-disabled-bg{background:var(--sd-disabled-bg)!important}.border-disabled-bg{border-color:var(--sd-disabled-bg)!important}.text-disabled-text{color:var(--sd-disabled-text)!important}.bg-disabled-text{background:var(--sd-disabled-text)!important}.border-disabled-text{border-color:var(--sd-disabled-text)!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}.text-secondary{color:var(--sd-text-secondary)!important}.text-muted{color:var(--sd-text-muted)!important}.text-body{color:var(--sd-text)!important}.bg-muted{background:var(--sd-surface-muted)!important}:host(.sd-bare){position:relative}.sd-inline-view{cursor:pointer;display:inline-flex;align-items:center;width:100%;padding:1px 6px;border-radius:4px;background-color:#0000000a;transition:background-color .15s ease}.sd-inline-view:hover{background-color:#00000014}.sd-inline-view .sd-inline-clear{display:none;align-items:center;margin-left:4px;padding:0;border:0;background:transparent;cursor:pointer;line-height:0;color:var(--sd-text-secondary, #6b7280);transition:color .15s ease}.sd-inline-view .sd-inline-clear sd-icon{font-size:16px;width:16px;height:16px}.sd-inline-view .sd-inline-clear:hover{color:var(--sd-error, #b32626)}.sd-inline-view:hover .sd-inline-clear{display:inline-flex}.sd-inline-editor{position:absolute;inset:0;opacity:0;pointer-events:none}.sd-inline-editor ::ng-deep *{pointer-events:none!important}:host{display:block}:host(.sd-has-label){padding-top:4px}:host(.sd-viewed){padding-top:0}:host ::ng-deep .mat-mdc-form-field.mat-form-field-appearance-outline.mat-form-field-disabled .mat-mdc-text-field-wrapper{background:var(--sd-surface-muted)}:host ::ng-deep .mat-mdc-form-field input.c-selected::placeholder{color:#000;opacity:1}:host ::ng-deep .mat-mdc-form-field input.c-selected:-ms-input-placeholder{color:#000}:host ::ng-deep .mat-mdc-form-field input.c-selected::-ms-input-placeholder{color:#000}:host ::ng-deep .mat-mdc-form-field input.mat-mdc-input-element:disabled{color:var(--sd-disabled-text)!important}:host ::ng-deep .mat-mdc-form-field input.mat-mdc-input-element:disabled.c-selected::placeholder{color:var(--sd-disabled-text)!important}:host ::ng-deep .mat-mdc-form-field input.mat-mdc-input-element:disabled.c-selected:-ms-input-placeholder{color:var(--sd-disabled-text)!important}:host ::ng-deep .mat-mdc-form-field input.mat-mdc-input-element:disabled.c-selected::-ms-input-placeholder{color:var(--sd-disabled-text)!important}:host ::ng-deep .mat-mdc-form-field .mat-mdc-placeholder-required{color:var(--sd-error)}:host ::ng-deep .mat-mdc-form-field:hover .icon-copy{opacity:1}:host ::ng-deep .mat-mdc-form-field .icon-copy{cursor:pointer;width:.9em;height:.9em;fill:#00000080;transition:opacity .2s linear;opacity:0}::ng-deep .sd-autocomplete-panel .mat-mdc-option{min-height:36px!important;padding:8px 12px!important}::ng-deep .sd-autocomplete-panel .mdc-list-item__primary-text{font-size:14px!important;line-height:normal}::ng-deep .sd-autocomplete-panel .mat-pseudo-checkbox{transform:scale(.7);margin-right:8px!important}::ng-deep .sd-autocomplete-panel .mat-pseudo-checkbox-minimal{transform:scale(.7);transform-origin:center right}.sd__option--add{position:sticky;bottom:0;background-color:#fff;z-index:10;color:#000000de;cursor:pointer!important}.c-loading-icon{position:absolute;right:5px;top:5px}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i4.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatAutocompleteModule }, { kind: "component", type: i5.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "component", type: i5.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: i5.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i6.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: SdLabel, selector: "sd-label", inputs: ["label", "description", "required", "helperText"] }, { kind: "component", type: SdView, selector: "sd-view", inputs: ["label", "value", "display", "hyperlink", "labelTemplate", "valueTemplate", "selectedItems"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
457
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SdAutocomplete, isStandalone: true, selector: "sd-autocomplete", inputs: { autoIdInput: { classPropertyName: "autoIdInput", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, helperText: { classPropertyName: "helperText", publicName: "helperText", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, valueField: { classPropertyName: "valueField", publicName: "valueField", isSignal: true, isRequired: false, transformFunction: null }, displayField: { classPropertyName: "displayField", publicName: "displayField", isSignal: true, isRequired: false, transformFunction: null }, disabledField: { classPropertyName: "disabledField", publicName: "disabledField", isSignal: true, isRequired: false, transformFunction: null }, limit: { classPropertyName: "limit", publicName: "limit", isSignal: true, isRequired: false, transformFunction: null }, cacheChecksum: { classPropertyName: "cacheChecksum", publicName: "cacheChecksum", isSignal: true, isRequired: false, transformFunction: null }, hyperlink: { classPropertyName: "hyperlink", publicName: "hyperlink", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, hideInlineError: { classPropertyName: "hideInlineError", publicName: "hideInlineError", isSignal: true, isRequired: false, transformFunction: null }, addable: { classPropertyName: "addable", publicName: "addable", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, viewed: { classPropertyName: "viewed", publicName: "viewed", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, validator: { classPropertyName: "validator", publicName: "validator", isSignal: true, isRequired: false, transformFunction: null }, inlineError: { classPropertyName: "inlineError", publicName: "inlineError", isSignal: true, isRequired: false, transformFunction: null }, appearanceInput: { classPropertyName: "appearanceInput", publicName: "appearance", isSignal: true, isRequired: false, transformFunction: null }, valueModel: { classPropertyName: "valueModel", publicName: "model", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueModel: "modelChange", sdChange: "sdChange", sdSelection: "sdSelection", sdAdd: "sdAdd" }, host: { properties: { "class.sd-has-label": "!!label()", "class.sd-viewed": "isViewed() || isInline()", "class.sd-bare": "isInline()" } }, queries: [{ propertyName: "sdLabelTemplate", first: true, predicate: ["sdLabel"], descendants: true, isSignal: true }, { propertyName: "sdValueTemplate", first: true, predicate: ["sdValue"], descendants: true, isSignal: true }, { propertyName: "itemDef", first: true, predicate: SdItemDefDefDirective, descendants: true, isSignal: true }, { propertyName: "sdViewDef", first: true, predicate: SdViewDefDirective, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["input"], descendants: true, isSignal: true }, { propertyName: "autocompleteTrigger", first: true, predicate: MatAutocompleteTrigger, descendants: true, isSignal: true }], ngImport: i0, template: "@let _label = label();\r\n@let _appearance = appearance();\r\n@let _hideInlineError = hideInlineError();\r\n@let _errorMessage = errorMessage();\r\n@let _helperText = helperText();\r\n@let _required = required();\r\n@let _displayField = displayField();\r\n@let _disabledField = disabledField();\r\n@let iDefTpl = itemDef()?.templateRef;\r\n@let _loading = loading();\r\n@let nVal = $any(normalizedValue());\r\n@let _isInline = isInline();\r\n@let _bare = _isInline;\r\n@let _hasValue = nVal !== undefined && nVal !== null && nVal !== '';\r\n<!-- why: m\u1ED9t c\u1ED5ng gate duy nh\u1EA5t cho <mat-error>, aria-invalid v\u00E0 aria-describedby. -->\r\n@let _errorVisible = !!_errorMessage && formControl.touched;\r\n@let _showInlineError = !_hideInlineError && _errorVisible;\r\n\r\n@if (isViewed()) {\r\n <sd-view\r\n [label]=\"_label\"\r\n [labelTemplate]=\"sdLabelTemplate()\"\r\n [value]=\"nVal\"\r\n [display]=\"display()\"\r\n [hyperlink]=\"hyperlink()\"\r\n [valueTemplate]=\"viewTemplate()\">\r\n </sd-view>\r\n} @else {\r\n @if (_isInline) {\r\n <!-- why: inline \u2014 sd-view l\u00E0 \"m\u1EB7t\" + trigger; autocomplete editor render \u1EA9n (.sd-inline-editor) ch\u1EC9 \u0111\u1EC3 m\u1EDF panel. -->\r\n <span class=\"sd-inline-view\" role=\"button\" tabindex=\"0\" (click)=\"enterInlineEdit()\" (keydown.enter)=\"enterInlineEdit()\">\r\n <sd-view\r\n [label]=\"_label\"\r\n [labelTemplate]=\"sdLabelTemplate()\"\r\n [value]=\"nVal\"\r\n [display]=\"display()\"\r\n [hyperlink]=\"hyperlink()\"\r\n [valueTemplate]=\"viewTemplate()\">\r\n </sd-view>\r\n @if (clearable() && _hasValue && !_required && !formControl.disabled) {\r\n <button type=\"button\" class=\"sd-inline-clear\" (click)=\"clear($event)\" title=\"X\u00F3a\" aria-label=\"X\u00F3a\">\r\n <sd-icon name=\"close\"></sd-icon>\r\n </button>\r\n }\r\n </span>\r\n }\r\n\r\n @if (!_appearance) {\r\n <ng-content select=\"[sdLabel]\">\r\n @if (_label) {\r\n <sd-label [label]=\"_label\" [required]=\"_required\"></sd-label>\r\n }\r\n </ng-content>\r\n }\r\n\r\n <!-- why: div ch\u1EC9 l\u00E0 h\u1ED9p layout. aria-hidden=\"true\" c\u0169 \u1EA9n C\u1EA2 c\u00E2y con \u2014 label, <input>,\r\n <mat-error>, n\u00FAt clear v\u00E0 c\u1EA3 panel g\u1EE3i \u00FD \u2014 kh\u1ECFi accessibility tree. role=\"presentation\"\r\n ch\u1EC9 v\u00F4 hi\u1EC7u ho\u00E1 ch\u00EDnh div, KH\u00D4NG lan xu\u1ED1ng con; (click) l\u00E0 ti\u1EC7n \u00EDch chu\u1ED9t m\u00E0 b\u00E0n ph\u00EDm \u0111\u00E3\r\n c\u00F3 qua Tab v\u00E0o <input>. -->\r\n <div\r\n class=\"d-flex align-items-center\"\r\n role=\"presentation\"\r\n [class.sd-inline-editor]=\"_isInline\"\r\n [class.c-focused]=\"isFocused\"\r\n [class.c-disabled]=\"formControl.disabled\"\r\n (click)=\"onClick()\">\r\n <mat-form-field\r\n [class.sd-md]=\"size() === 'md'\"\r\n [class.sd-sm]=\"size() === 'sm'\"\r\n [class.hide-inline-error]=\"_hideInlineError\"\r\n [floatLabel]=\"nVal ? 'always' : 'auto'\"\r\n [appearance]=\"_appearance || 'outline'\">\r\n @if (_appearance && _label) {\r\n <mat-label class=\"sd-form-field-label\">\r\n <span>{{ _label }}</span>\r\n @if (_helperText) {\r\n <sd-icon [matTooltip]=\"_helperText\" matTooltipPosition=\"below\" name=\"info\"></sd-icon>\r\n }\r\n </mat-label>\r\n }\r\n\r\n <!-- why: B\u1ECE aria-hidden tr\u00EAn ch\u00EDnh <input> \u2014 \u00F4 Tab t\u1EDBi v\u00E0 g\u00F5 \u0111\u01B0\u1EE3c, aria-hidden ch\u1EC9 khi\u1EBFn\r\n screen reader \u0111\u1ECDc ra kho\u1EA3ng l\u1EB7ng. -->\r\n <input\r\n [id]=\"id\"\r\n [formControl]=\"inputControl\"\r\n [placeholder]=\"controlPlaceHolder() || ''\"\r\n [class.c-selected]=\"nVal\"\r\n [matAutocomplete]=\"auto\"\r\n (focus)=\"onFocus()\"\r\n (blur)=\"onBlur()\"\r\n matInput\r\n [autocomplete]=\"id\"\r\n autocorrect=\"off\"\r\n [errorStateMatcher]=\"matcher\"\r\n [required]=\"_required\"\r\n #input\r\n #autocompleteTrigger=\"matAutocompleteTrigger\"\r\n [attr.data-autoId]=\"autoId()\"\r\n [attr.data-disabled]=\"dataDisabled()\"\r\n [attr.data-invalid]=\"dataInvalid()\"\r\n [attr.data-empty]=\"dataEmpty()\"\r\n [attr.data-value]=\"dataValue()\"\r\n [attr.data-loading]=\"dataLoading()\"\r\n [attr.data-required]=\"dataRequired()\"\r\n [attr.data-error-message]=\"dataErrorMessage()\"\r\n [attr.aria-invalid]=\"_errorVisible ? 'true' : null\"\r\n [attr.aria-describedby]=\"_errorMessage ? errorId : null\" />\r\n\r\n @if (!_loading && nVal && !inputControl.disabled && !_bare) {\r\n <button type=\"button\" class=\"sd-clear-btn\" matSuffix (click)=\"clear($event)\" title=\"X\u00F3a\" aria-label=\"X\u00F3a\">\r\n <sd-icon name=\"close\"></sd-icon>\r\n </button>\r\n } @else if (_loading) {\r\n <mat-spinner [diameter]=\"20\" matSuffix></mat-spinner>\r\n } @else {\r\n <sd-icon class=\"cursor-pointer sd-suffix-icon\" matSuffix name=\"search\"></sd-icon>\r\n }\r\n\r\n <mat-autocomplete #auto=\"matAutocomplete\" (optionSelected)=\"onSelect($event.option.value)\" class=\"sd-autocomplete-panel\">\r\n @let _filteredItems = filteredItems();\r\n\r\n @if (_filteredItems?.length) {\r\n @for (item of _filteredItems; track $index) {\r\n <mat-option\r\n [value]=\"item\"\r\n matTooltipPosition=\"above\"\r\n [matTooltip]=\"_displayField ? getNestedValue(item, _displayField) : item\"\r\n [disabled]=\"_disabledField ? getNestedValue(item, _disabledField) : false\">\r\n @if (iDefTpl) {\r\n <ng-container *ngTemplateOutlet=\"iDefTpl ?? null; context: { item: item }\"> </ng-container>\r\n } @else {\r\n {{ _displayField ? getNestedValue(item, _displayField) : item }}\r\n }\r\n </mat-option>\r\n }\r\n } @else if (!_filteredItems?.length && inputControl.value && !isTyping() && !_loading) {}\r\n\r\n @if (addable() && sdAdd.observed) {\r\n <mat-option class=\"sd__option--add\" (keyup.Space)=\"$event.stopPropagation()\" disabled=\"true\">\r\n <!-- why: tr\u01B0\u1EDBc l\u00E0 <div (click)> \u2014 kh\u00F4ng Tab t\u1EDBi \u0111\u01B0\u1EE3c, kh\u00F4ng c\u00F3 role, v\u00E0 ch\u1EC9 \"im l\u1EB7ng\"\r\n v\u1EDBi lint v\u00EC c\u1EA3 c\u1EE5m n\u1EB1m trong c\u00E2y aria-hidden. <button type=\"button\"> l\u1EA5y s\u1EB5n\r\n Enter/Space + role button, kh\u00F4ng ph\u1EA3i b\u1ECBa tabindex/keydown th\u1EE7 c\u00F4ng. -->\r\n <button type=\"button\" class=\"sd__option--add-btn\" (click)=\"onAdd($event)\">\r\n <sd-icon class=\"mr-1\" name=\"add\" aria-hidden=\"true\"></sd-icon>\r\n {{ 'New item' }}\r\n </button>\r\n </mat-option>\r\n }\r\n </mat-autocomplete>\r\n\r\n @if (_hideInlineError && _errorVisible) {\r\n <sd-icon matSuffix class=\"sd-error-icon\" [matTooltip]=\"_errorMessage\" matTooltipPosition=\"above\" name=\"error\"></sd-icon>\n }\r\n @if (_hideInlineError && _errorVisible) {\r\n <span class=\"sd-visually-hidden\" [id]=\"errorId\" matSuffix>{{ _errorMessage }}</span>\r\r\n }\r\n\r\n @if (_showInlineError) {\r\n <mat-error [id]=\"errorId\">{{ _errorMessage }}</mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n}\r\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-primary-contrast{color:var(--sd-primary-contrast)!important}.bg-primary-contrast{background:var(--sd-primary-contrast)!important}.border-primary-contrast{border-color:var(--sd-primary-contrast)!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-secondary-contrast{color:var(--sd-secondary-contrast)!important}.bg-secondary-contrast{background:var(--sd-secondary-contrast)!important}.border-secondary-contrast{border-color:var(--sd-secondary-contrast)!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-info-contrast{color:var(--sd-info-contrast)!important}.bg-info-contrast{background:var(--sd-info-contrast)!important}.border-info-contrast{border-color:var(--sd-info-contrast)!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-success-contrast{color:var(--sd-success-contrast)!important}.bg-success-contrast{background:var(--sd-success-contrast)!important}.border-success-contrast{border-color:var(--sd-success-contrast)!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-warning-contrast{color:var(--sd-warning-contrast)!important}.bg-warning-contrast{background:var(--sd-warning-contrast)!important}.border-warning-contrast{border-color:var(--sd-warning-contrast)!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-error-contrast{color:var(--sd-error-contrast)!important}.bg-error-contrast{background:var(--sd-error-contrast)!important}.border-error-contrast{border-color:var(--sd-error-contrast)!important}.text-surface{color:var(--sd-surface)!important}.bg-surface{background:var(--sd-surface)!important}.border-surface{border-color:var(--sd-surface)!important}.text-surface-muted{color:var(--sd-surface-muted)!important}.bg-surface-muted{background:var(--sd-surface-muted)!important}.border-surface-muted{border-color:var(--sd-surface-muted)!important}.text-text{color:var(--sd-text)!important}.bg-text{background:var(--sd-text)!important}.border-text{border-color:var(--sd-text)!important}.text-text-secondary{color:var(--sd-text-secondary)!important}.bg-text-secondary{background:var(--sd-text-secondary)!important}.border-text-secondary{border-color:var(--sd-text-secondary)!important}.text-text-muted{color:var(--sd-text-muted)!important}.bg-text-muted{background:var(--sd-text-muted)!important}.border-text-muted{border-color:var(--sd-text-muted)!important}.text-border{color:var(--sd-border)!important}.bg-border{background:var(--sd-border)!important}.border-border{border-color:var(--sd-border)!important}.text-border-strong{color:var(--sd-border-strong)!important}.bg-border-strong{background:var(--sd-border-strong)!important}.border-border-strong{border-color:var(--sd-border-strong)!important}.text-disabled-bg{color:var(--sd-disabled-bg)!important}.bg-disabled-bg{background:var(--sd-disabled-bg)!important}.border-disabled-bg{border-color:var(--sd-disabled-bg)!important}.text-disabled-text{color:var(--sd-disabled-text)!important}.bg-disabled-text{background:var(--sd-disabled-text)!important}.border-disabled-text{border-color:var(--sd-disabled-text)!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}.text-secondary{color:var(--sd-text-secondary)!important}.text-muted{color:var(--sd-text-muted)!important}.text-body{color:var(--sd-text)!important}.bg-muted{background:var(--sd-surface-muted)!important}:host(.sd-bare){position:relative}.sd-inline-view{cursor:pointer;display:inline-flex;align-items:center;width:100%;padding:1px 6px;border-radius:4px;background-color:#0000000a;transition:background-color .15s ease}.sd-inline-view:hover{background-color:#00000014}.sd-inline-view .sd-inline-clear{display:none;align-items:center;margin-left:4px;padding:0;border:0;background:transparent;cursor:pointer;line-height:0;color:var(--sd-text-secondary, #6b7280);transition:color .15s ease}.sd-inline-view .sd-inline-clear sd-icon{font-size:16px;width:16px;height:16px}.sd-inline-view .sd-inline-clear:hover{color:var(--sd-error, #b32626)}.sd-inline-view:hover .sd-inline-clear{display:inline-flex}.sd-inline-editor{position:absolute;inset:0;opacity:0;pointer-events:none}.sd-inline-editor ::ng-deep *{pointer-events:none!important}:host{display:block}:host(.sd-has-label){padding-top:4px}:host(.sd-viewed){padding-top:0}:host ::ng-deep .mat-mdc-form-field.mat-form-field-appearance-outline.mat-form-field-disabled .mat-mdc-text-field-wrapper{background:var(--sd-surface-muted)}:host ::ng-deep .mat-mdc-form-field input.c-selected::placeholder{color:#000;opacity:1}:host ::ng-deep .mat-mdc-form-field input.c-selected:-ms-input-placeholder{color:#000}:host ::ng-deep .mat-mdc-form-field input.c-selected::-ms-input-placeholder{color:#000}:host ::ng-deep .mat-mdc-form-field input.mat-mdc-input-element:disabled{color:var(--sd-disabled-text)!important}:host ::ng-deep .mat-mdc-form-field input.mat-mdc-input-element:disabled.c-selected::placeholder{color:var(--sd-disabled-text)!important}:host ::ng-deep .mat-mdc-form-field input.mat-mdc-input-element:disabled.c-selected:-ms-input-placeholder{color:var(--sd-disabled-text)!important}:host ::ng-deep .mat-mdc-form-field input.mat-mdc-input-element:disabled.c-selected::-ms-input-placeholder{color:var(--sd-disabled-text)!important}:host ::ng-deep .mat-mdc-form-field .mat-mdc-placeholder-required{color:var(--sd-error)}:host ::ng-deep .mat-mdc-form-field:hover .icon-copy{opacity:1}:host ::ng-deep .mat-mdc-form-field .icon-copy{cursor:pointer;width:.9em;height:.9em;fill:#00000080;transition:opacity .2s linear;opacity:0}::ng-deep .sd-autocomplete-panel .mat-mdc-option{min-height:36px!important;padding:8px 12px!important}::ng-deep .sd-autocomplete-panel .mdc-list-item__primary-text{font-size:14px!important;line-height:normal}::ng-deep .sd-autocomplete-panel .mat-pseudo-checkbox{transform:scale(.7);margin-right:8px!important}::ng-deep .sd-autocomplete-panel .mat-pseudo-checkbox-minimal{transform:scale(.7);transform-origin:center right}.sd__option--add{position:sticky;bottom:0;background-color:#fff;z-index:10;color:#000000de;cursor:pointer!important}.sd__option--add-btn{display:flex;align-items:center;width:100%;border:0;padding:0;background:transparent;font:inherit;color:inherit;text-align:left;cursor:pointer}.c-loading-icon{position:absolute;right:5px;top:5px}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i4.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatAutocompleteModule }, { kind: "component", type: i5.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "component", type: i5.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: i5.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i6.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: SdLabel, selector: "sd-label", inputs: ["label", "description", "required", "helperText"] }, { kind: "component", type: SdView, selector: "sd-view", inputs: ["label", "value", "display", "hyperlink", "labelTemplate", "valueTemplate", "selectedItems"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
447
458
  }
448
459
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdAutocomplete, decorators: [{
449
460
  type: Component,
@@ -459,7 +470,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
459
470
  MatProgressSpinnerModule,
460
471
  SdLabel,
461
472
  SdView,
462
- ], template: "@let _label = label();\n@let _appearance = appearance();\n@let _hideInlineError = hideInlineError();\n@let _errorMessage = errorMessage();\n@let _helperText = helperText();\n@let _required = required();\n@let _displayField = displayField();\n@let _disabledField = disabledField();\n@let iDefTpl = itemDef()?.templateRef;\n@let _loading = loading();\n@let nVal = $any(normalizedValue());\n@let _isInline = isInline();\n@let _bare = _isInline;\n@let _hasValue = nVal !== undefined && nVal !== null && nVal !== '';\n\n@if (isViewed()) {\n <sd-view\n [label]=\"_label\"\n [labelTemplate]=\"sdLabelTemplate()\"\n [value]=\"nVal\"\n [display]=\"display()\"\n [hyperlink]=\"hyperlink()\"\n [valueTemplate]=\"viewTemplate()\">\n </sd-view>\n} @else {\n @if (_isInline) {\n <!-- why: inline \u2014 sd-view l\u00E0 \"m\u1EB7t\" + trigger; autocomplete editor render \u1EA9n (.sd-inline-editor) ch\u1EC9 \u0111\u1EC3 m\u1EDF panel. -->\n <span class=\"sd-inline-view\" role=\"button\" tabindex=\"0\" (click)=\"enterInlineEdit()\" (keydown.enter)=\"enterInlineEdit()\">\n <sd-view\n [label]=\"_label\"\n [labelTemplate]=\"sdLabelTemplate()\"\n [value]=\"nVal\"\n [display]=\"display()\"\n [hyperlink]=\"hyperlink()\"\n [valueTemplate]=\"viewTemplate()\">\n </sd-view>\n @if (clearable() && _hasValue && !_required && !formControl.disabled) {\n <button type=\"button\" class=\"sd-inline-clear\" (click)=\"clear($event)\" title=\"X\u00F3a\" aria-label=\"X\u00F3a\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n }\n </span>\n }\n\n @if (!_appearance) {\n <ng-content select=\"[sdLabel]\">\n @if (_label) {\n <sd-label [label]=\"_label\" [required]=\"_required\"></sd-label>\n }\n </ng-content>\n }\n\n <div\n class=\"d-flex align-items-center\"\n [class.sd-inline-editor]=\"_isInline\"\n [class.c-focused]=\"isFocused\"\n [class.c-disabled]=\"formControl.disabled\"\n (click)=\"onClick()\"\n aria-hidden=\"true\">\n <mat-form-field\n [class.sd-md]=\"size() === 'md'\"\n [class.sd-sm]=\"size() === 'sm'\"\n [class.hide-inline-error]=\"_hideInlineError\"\n [floatLabel]=\"nVal ? 'always' : 'auto'\"\n [appearance]=\"_appearance || 'outline'\">\n @if (_appearance && _label) {\n <mat-label class=\"sd-form-field-label\">\n <span>{{ _label }}</span>\n @if (_helperText) {\n <sd-icon [matTooltip]=\"_helperText\" matTooltipPosition=\"below\" name=\"info\"></sd-icon>\n }\n </mat-label>\n }\n\n <input\n [id]=\"id\"\n [formControl]=\"inputControl\"\n [placeholder]=\"controlPlaceHolder() || ''\"\n [class.c-selected]=\"nVal\"\n [matAutocomplete]=\"auto\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n matInput\n [autocomplete]=\"id\"\n autocorrect=\"off\"\n [errorStateMatcher]=\"matcher\"\n [required]=\"_required\"\n #input\n #autocompleteTrigger=\"matAutocompleteTrigger\"\n [attr.data-autoId]=\"autoId()\"\n [attr.data-disabled]=\"dataDisabled()\"\n [attr.data-invalid]=\"dataInvalid()\"\n [attr.data-empty]=\"dataEmpty()\"\n [attr.data-value]=\"dataValue()\"\n [attr.data-loading]=\"dataLoading()\"\n [attr.data-required]=\"dataRequired()\"\n [attr.data-error-message]=\"dataErrorMessage()\"\n aria-hidden=\"true\" />\n\n @if (!_loading && nVal && !inputControl.disabled && !_bare) {\n <button type=\"button\" class=\"sd-clear-btn\" matSuffix (click)=\"clear($event)\" title=\"X\u00F3a\" aria-label=\"X\u00F3a\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n } @else if (_loading) {\n <mat-spinner [diameter]=\"20\" matSuffix></mat-spinner>\n } @else {\n <sd-icon class=\"cursor-pointer sd-suffix-icon\" matSuffix name=\"search\"></sd-icon>\n }\n\n <mat-autocomplete #auto=\"matAutocomplete\" (optionSelected)=\"onSelect($event.option.value)\" class=\"sd-autocomplete-panel\">\n @let _filteredItems = filteredItems();\n\n @if (_filteredItems?.length) {\n @for (item of _filteredItems; track $index) {\n <mat-option\n [value]=\"item\"\n matTooltipPosition=\"above\"\n [matTooltip]=\"_displayField ? getNestedValue(item, _displayField) : item\"\n [disabled]=\"_disabledField ? getNestedValue(item, _disabledField) : false\">\n @if (iDefTpl) {\n <ng-container *ngTemplateOutlet=\"iDefTpl ?? null; context: { item: item }\"> </ng-container>\n } @else {\n {{ _displayField ? getNestedValue(item, _displayField) : item }}\n }\n </mat-option>\n }\n } @else if (!_filteredItems?.length && inputControl.value && !isTyping() && !_loading) {}\n\n @if (addable() && sdAdd.observed) {\n <mat-option class=\"sd__option--add\" (keyup.Space)=\"$event.stopPropagation()\" disabled=\"true\">\n <div (click)=\"onAdd($event)\">\n <sd-icon class=\"mr-1\" name=\"add\"></sd-icon>\n {{ 'New item' }}\n </div>\n </mat-option>\n }\n </mat-autocomplete>\n\n @if (_hideInlineError && _errorMessage && formControl.touched) {\n <sd-icon matSuffix class=\"sd-error-icon\" [matTooltip]=\"_errorMessage\" matTooltipPosition=\"above\" name=\"error\"></sd-icon>\n }\n\n @if (!_hideInlineError && _errorMessage && formControl.touched) {\n <mat-error>{{ _errorMessage }}</mat-error>\n }\n </mat-form-field>\n </div>\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-primary-contrast{color:var(--sd-primary-contrast)!important}.bg-primary-contrast{background:var(--sd-primary-contrast)!important}.border-primary-contrast{border-color:var(--sd-primary-contrast)!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-secondary-contrast{color:var(--sd-secondary-contrast)!important}.bg-secondary-contrast{background:var(--sd-secondary-contrast)!important}.border-secondary-contrast{border-color:var(--sd-secondary-contrast)!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-info-contrast{color:var(--sd-info-contrast)!important}.bg-info-contrast{background:var(--sd-info-contrast)!important}.border-info-contrast{border-color:var(--sd-info-contrast)!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-success-contrast{color:var(--sd-success-contrast)!important}.bg-success-contrast{background:var(--sd-success-contrast)!important}.border-success-contrast{border-color:var(--sd-success-contrast)!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-warning-contrast{color:var(--sd-warning-contrast)!important}.bg-warning-contrast{background:var(--sd-warning-contrast)!important}.border-warning-contrast{border-color:var(--sd-warning-contrast)!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-error-contrast{color:var(--sd-error-contrast)!important}.bg-error-contrast{background:var(--sd-error-contrast)!important}.border-error-contrast{border-color:var(--sd-error-contrast)!important}.text-surface{color:var(--sd-surface)!important}.bg-surface{background:var(--sd-surface)!important}.border-surface{border-color:var(--sd-surface)!important}.text-surface-muted{color:var(--sd-surface-muted)!important}.bg-surface-muted{background:var(--sd-surface-muted)!important}.border-surface-muted{border-color:var(--sd-surface-muted)!important}.text-text{color:var(--sd-text)!important}.bg-text{background:var(--sd-text)!important}.border-text{border-color:var(--sd-text)!important}.text-text-secondary{color:var(--sd-text-secondary)!important}.bg-text-secondary{background:var(--sd-text-secondary)!important}.border-text-secondary{border-color:var(--sd-text-secondary)!important}.text-text-muted{color:var(--sd-text-muted)!important}.bg-text-muted{background:var(--sd-text-muted)!important}.border-text-muted{border-color:var(--sd-text-muted)!important}.text-border{color:var(--sd-border)!important}.bg-border{background:var(--sd-border)!important}.border-border{border-color:var(--sd-border)!important}.text-border-strong{color:var(--sd-border-strong)!important}.bg-border-strong{background:var(--sd-border-strong)!important}.border-border-strong{border-color:var(--sd-border-strong)!important}.text-disabled-bg{color:var(--sd-disabled-bg)!important}.bg-disabled-bg{background:var(--sd-disabled-bg)!important}.border-disabled-bg{border-color:var(--sd-disabled-bg)!important}.text-disabled-text{color:var(--sd-disabled-text)!important}.bg-disabled-text{background:var(--sd-disabled-text)!important}.border-disabled-text{border-color:var(--sd-disabled-text)!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}.text-secondary{color:var(--sd-text-secondary)!important}.text-muted{color:var(--sd-text-muted)!important}.text-body{color:var(--sd-text)!important}.bg-muted{background:var(--sd-surface-muted)!important}:host(.sd-bare){position:relative}.sd-inline-view{cursor:pointer;display:inline-flex;align-items:center;width:100%;padding:1px 6px;border-radius:4px;background-color:#0000000a;transition:background-color .15s ease}.sd-inline-view:hover{background-color:#00000014}.sd-inline-view .sd-inline-clear{display:none;align-items:center;margin-left:4px;padding:0;border:0;background:transparent;cursor:pointer;line-height:0;color:var(--sd-text-secondary, #6b7280);transition:color .15s ease}.sd-inline-view .sd-inline-clear sd-icon{font-size:16px;width:16px;height:16px}.sd-inline-view .sd-inline-clear:hover{color:var(--sd-error, #b32626)}.sd-inline-view:hover .sd-inline-clear{display:inline-flex}.sd-inline-editor{position:absolute;inset:0;opacity:0;pointer-events:none}.sd-inline-editor ::ng-deep *{pointer-events:none!important}:host{display:block}:host(.sd-has-label){padding-top:4px}:host(.sd-viewed){padding-top:0}:host ::ng-deep .mat-mdc-form-field.mat-form-field-appearance-outline.mat-form-field-disabled .mat-mdc-text-field-wrapper{background:var(--sd-surface-muted)}:host ::ng-deep .mat-mdc-form-field input.c-selected::placeholder{color:#000;opacity:1}:host ::ng-deep .mat-mdc-form-field input.c-selected:-ms-input-placeholder{color:#000}:host ::ng-deep .mat-mdc-form-field input.c-selected::-ms-input-placeholder{color:#000}:host ::ng-deep .mat-mdc-form-field input.mat-mdc-input-element:disabled{color:var(--sd-disabled-text)!important}:host ::ng-deep .mat-mdc-form-field input.mat-mdc-input-element:disabled.c-selected::placeholder{color:var(--sd-disabled-text)!important}:host ::ng-deep .mat-mdc-form-field input.mat-mdc-input-element:disabled.c-selected:-ms-input-placeholder{color:var(--sd-disabled-text)!important}:host ::ng-deep .mat-mdc-form-field input.mat-mdc-input-element:disabled.c-selected::-ms-input-placeholder{color:var(--sd-disabled-text)!important}:host ::ng-deep .mat-mdc-form-field .mat-mdc-placeholder-required{color:var(--sd-error)}:host ::ng-deep .mat-mdc-form-field:hover .icon-copy{opacity:1}:host ::ng-deep .mat-mdc-form-field .icon-copy{cursor:pointer;width:.9em;height:.9em;fill:#00000080;transition:opacity .2s linear;opacity:0}::ng-deep .sd-autocomplete-panel .mat-mdc-option{min-height:36px!important;padding:8px 12px!important}::ng-deep .sd-autocomplete-panel .mdc-list-item__primary-text{font-size:14px!important;line-height:normal}::ng-deep .sd-autocomplete-panel .mat-pseudo-checkbox{transform:scale(.7);margin-right:8px!important}::ng-deep .sd-autocomplete-panel .mat-pseudo-checkbox-minimal{transform:scale(.7);transform-origin:center right}.sd__option--add{position:sticky;bottom:0;background-color:#fff;z-index:10;color:#000000de;cursor:pointer!important}.c-loading-icon{position:absolute;right:5px;top:5px}\n"] }]
473
+ ], template: "@let _label = label();\r\n@let _appearance = appearance();\r\n@let _hideInlineError = hideInlineError();\r\n@let _errorMessage = errorMessage();\r\n@let _helperText = helperText();\r\n@let _required = required();\r\n@let _displayField = displayField();\r\n@let _disabledField = disabledField();\r\n@let iDefTpl = itemDef()?.templateRef;\r\n@let _loading = loading();\r\n@let nVal = $any(normalizedValue());\r\n@let _isInline = isInline();\r\n@let _bare = _isInline;\r\n@let _hasValue = nVal !== undefined && nVal !== null && nVal !== '';\r\n<!-- why: m\u1ED9t c\u1ED5ng gate duy nh\u1EA5t cho <mat-error>, aria-invalid v\u00E0 aria-describedby. -->\r\n@let _errorVisible = !!_errorMessage && formControl.touched;\r\n@let _showInlineError = !_hideInlineError && _errorVisible;\r\n\r\n@if (isViewed()) {\r\n <sd-view\r\n [label]=\"_label\"\r\n [labelTemplate]=\"sdLabelTemplate()\"\r\n [value]=\"nVal\"\r\n [display]=\"display()\"\r\n [hyperlink]=\"hyperlink()\"\r\n [valueTemplate]=\"viewTemplate()\">\r\n </sd-view>\r\n} @else {\r\n @if (_isInline) {\r\n <!-- why: inline \u2014 sd-view l\u00E0 \"m\u1EB7t\" + trigger; autocomplete editor render \u1EA9n (.sd-inline-editor) ch\u1EC9 \u0111\u1EC3 m\u1EDF panel. -->\r\n <span class=\"sd-inline-view\" role=\"button\" tabindex=\"0\" (click)=\"enterInlineEdit()\" (keydown.enter)=\"enterInlineEdit()\">\r\n <sd-view\r\n [label]=\"_label\"\r\n [labelTemplate]=\"sdLabelTemplate()\"\r\n [value]=\"nVal\"\r\n [display]=\"display()\"\r\n [hyperlink]=\"hyperlink()\"\r\n [valueTemplate]=\"viewTemplate()\">\r\n </sd-view>\r\n @if (clearable() && _hasValue && !_required && !formControl.disabled) {\r\n <button type=\"button\" class=\"sd-inline-clear\" (click)=\"clear($event)\" title=\"X\u00F3a\" aria-label=\"X\u00F3a\">\r\n <sd-icon name=\"close\"></sd-icon>\r\n </button>\r\n }\r\n </span>\r\n }\r\n\r\n @if (!_appearance) {\r\n <ng-content select=\"[sdLabel]\">\r\n @if (_label) {\r\n <sd-label [label]=\"_label\" [required]=\"_required\"></sd-label>\r\n }\r\n </ng-content>\r\n }\r\n\r\n <!-- why: div ch\u1EC9 l\u00E0 h\u1ED9p layout. aria-hidden=\"true\" c\u0169 \u1EA9n C\u1EA2 c\u00E2y con \u2014 label, <input>,\r\n <mat-error>, n\u00FAt clear v\u00E0 c\u1EA3 panel g\u1EE3i \u00FD \u2014 kh\u1ECFi accessibility tree. role=\"presentation\"\r\n ch\u1EC9 v\u00F4 hi\u1EC7u ho\u00E1 ch\u00EDnh div, KH\u00D4NG lan xu\u1ED1ng con; (click) l\u00E0 ti\u1EC7n \u00EDch chu\u1ED9t m\u00E0 b\u00E0n ph\u00EDm \u0111\u00E3\r\n c\u00F3 qua Tab v\u00E0o <input>. -->\r\n <div\r\n class=\"d-flex align-items-center\"\r\n role=\"presentation\"\r\n [class.sd-inline-editor]=\"_isInline\"\r\n [class.c-focused]=\"isFocused\"\r\n [class.c-disabled]=\"formControl.disabled\"\r\n (click)=\"onClick()\">\r\n <mat-form-field\r\n [class.sd-md]=\"size() === 'md'\"\r\n [class.sd-sm]=\"size() === 'sm'\"\r\n [class.hide-inline-error]=\"_hideInlineError\"\r\n [floatLabel]=\"nVal ? 'always' : 'auto'\"\r\n [appearance]=\"_appearance || 'outline'\">\r\n @if (_appearance && _label) {\r\n <mat-label class=\"sd-form-field-label\">\r\n <span>{{ _label }}</span>\r\n @if (_helperText) {\r\n <sd-icon [matTooltip]=\"_helperText\" matTooltipPosition=\"below\" name=\"info\"></sd-icon>\r\n }\r\n </mat-label>\r\n }\r\n\r\n <!-- why: B\u1ECE aria-hidden tr\u00EAn ch\u00EDnh <input> \u2014 \u00F4 Tab t\u1EDBi v\u00E0 g\u00F5 \u0111\u01B0\u1EE3c, aria-hidden ch\u1EC9 khi\u1EBFn\r\n screen reader \u0111\u1ECDc ra kho\u1EA3ng l\u1EB7ng. -->\r\n <input\r\n [id]=\"id\"\r\n [formControl]=\"inputControl\"\r\n [placeholder]=\"controlPlaceHolder() || ''\"\r\n [class.c-selected]=\"nVal\"\r\n [matAutocomplete]=\"auto\"\r\n (focus)=\"onFocus()\"\r\n (blur)=\"onBlur()\"\r\n matInput\r\n [autocomplete]=\"id\"\r\n autocorrect=\"off\"\r\n [errorStateMatcher]=\"matcher\"\r\n [required]=\"_required\"\r\n #input\r\n #autocompleteTrigger=\"matAutocompleteTrigger\"\r\n [attr.data-autoId]=\"autoId()\"\r\n [attr.data-disabled]=\"dataDisabled()\"\r\n [attr.data-invalid]=\"dataInvalid()\"\r\n [attr.data-empty]=\"dataEmpty()\"\r\n [attr.data-value]=\"dataValue()\"\r\n [attr.data-loading]=\"dataLoading()\"\r\n [attr.data-required]=\"dataRequired()\"\r\n [attr.data-error-message]=\"dataErrorMessage()\"\r\n [attr.aria-invalid]=\"_errorVisible ? 'true' : null\"\r\n [attr.aria-describedby]=\"_errorMessage ? errorId : null\" />\r\n\r\n @if (!_loading && nVal && !inputControl.disabled && !_bare) {\r\n <button type=\"button\" class=\"sd-clear-btn\" matSuffix (click)=\"clear($event)\" title=\"X\u00F3a\" aria-label=\"X\u00F3a\">\r\n <sd-icon name=\"close\"></sd-icon>\r\n </button>\r\n } @else if (_loading) {\r\n <mat-spinner [diameter]=\"20\" matSuffix></mat-spinner>\r\n } @else {\r\n <sd-icon class=\"cursor-pointer sd-suffix-icon\" matSuffix name=\"search\"></sd-icon>\r\n }\r\n\r\n <mat-autocomplete #auto=\"matAutocomplete\" (optionSelected)=\"onSelect($event.option.value)\" class=\"sd-autocomplete-panel\">\r\n @let _filteredItems = filteredItems();\r\n\r\n @if (_filteredItems?.length) {\r\n @for (item of _filteredItems; track $index) {\r\n <mat-option\r\n [value]=\"item\"\r\n matTooltipPosition=\"above\"\r\n [matTooltip]=\"_displayField ? getNestedValue(item, _displayField) : item\"\r\n [disabled]=\"_disabledField ? getNestedValue(item, _disabledField) : false\">\r\n @if (iDefTpl) {\r\n <ng-container *ngTemplateOutlet=\"iDefTpl ?? null; context: { item: item }\"> </ng-container>\r\n } @else {\r\n {{ _displayField ? getNestedValue(item, _displayField) : item }}\r\n }\r\n </mat-option>\r\n }\r\n } @else if (!_filteredItems?.length && inputControl.value && !isTyping() && !_loading) {}\r\n\r\n @if (addable() && sdAdd.observed) {\r\n <mat-option class=\"sd__option--add\" (keyup.Space)=\"$event.stopPropagation()\" disabled=\"true\">\r\n <!-- why: tr\u01B0\u1EDBc l\u00E0 <div (click)> \u2014 kh\u00F4ng Tab t\u1EDBi \u0111\u01B0\u1EE3c, kh\u00F4ng c\u00F3 role, v\u00E0 ch\u1EC9 \"im l\u1EB7ng\"\r\n v\u1EDBi lint v\u00EC c\u1EA3 c\u1EE5m n\u1EB1m trong c\u00E2y aria-hidden. <button type=\"button\"> l\u1EA5y s\u1EB5n\r\n Enter/Space + role button, kh\u00F4ng ph\u1EA3i b\u1ECBa tabindex/keydown th\u1EE7 c\u00F4ng. -->\r\n <button type=\"button\" class=\"sd__option--add-btn\" (click)=\"onAdd($event)\">\r\n <sd-icon class=\"mr-1\" name=\"add\" aria-hidden=\"true\"></sd-icon>\r\n {{ 'New item' }}\r\n </button>\r\n </mat-option>\r\n }\r\n </mat-autocomplete>\r\n\r\n @if (_hideInlineError && _errorVisible) {\r\n <sd-icon matSuffix class=\"sd-error-icon\" [matTooltip]=\"_errorMessage\" matTooltipPosition=\"above\" name=\"error\"></sd-icon>\n }\r\n @if (_hideInlineError && _errorVisible) {\r\n <span class=\"sd-visually-hidden\" [id]=\"errorId\" matSuffix>{{ _errorMessage }}</span>\r\r\n }\r\n\r\n @if (_showInlineError) {\r\n <mat-error [id]=\"errorId\">{{ _errorMessage }}</mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n}\r\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-primary-contrast{color:var(--sd-primary-contrast)!important}.bg-primary-contrast{background:var(--sd-primary-contrast)!important}.border-primary-contrast{border-color:var(--sd-primary-contrast)!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-secondary-contrast{color:var(--sd-secondary-contrast)!important}.bg-secondary-contrast{background:var(--sd-secondary-contrast)!important}.border-secondary-contrast{border-color:var(--sd-secondary-contrast)!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-info-contrast{color:var(--sd-info-contrast)!important}.bg-info-contrast{background:var(--sd-info-contrast)!important}.border-info-contrast{border-color:var(--sd-info-contrast)!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-success-contrast{color:var(--sd-success-contrast)!important}.bg-success-contrast{background:var(--sd-success-contrast)!important}.border-success-contrast{border-color:var(--sd-success-contrast)!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-warning-contrast{color:var(--sd-warning-contrast)!important}.bg-warning-contrast{background:var(--sd-warning-contrast)!important}.border-warning-contrast{border-color:var(--sd-warning-contrast)!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-error-contrast{color:var(--sd-error-contrast)!important}.bg-error-contrast{background:var(--sd-error-contrast)!important}.border-error-contrast{border-color:var(--sd-error-contrast)!important}.text-surface{color:var(--sd-surface)!important}.bg-surface{background:var(--sd-surface)!important}.border-surface{border-color:var(--sd-surface)!important}.text-surface-muted{color:var(--sd-surface-muted)!important}.bg-surface-muted{background:var(--sd-surface-muted)!important}.border-surface-muted{border-color:var(--sd-surface-muted)!important}.text-text{color:var(--sd-text)!important}.bg-text{background:var(--sd-text)!important}.border-text{border-color:var(--sd-text)!important}.text-text-secondary{color:var(--sd-text-secondary)!important}.bg-text-secondary{background:var(--sd-text-secondary)!important}.border-text-secondary{border-color:var(--sd-text-secondary)!important}.text-text-muted{color:var(--sd-text-muted)!important}.bg-text-muted{background:var(--sd-text-muted)!important}.border-text-muted{border-color:var(--sd-text-muted)!important}.text-border{color:var(--sd-border)!important}.bg-border{background:var(--sd-border)!important}.border-border{border-color:var(--sd-border)!important}.text-border-strong{color:var(--sd-border-strong)!important}.bg-border-strong{background:var(--sd-border-strong)!important}.border-border-strong{border-color:var(--sd-border-strong)!important}.text-disabled-bg{color:var(--sd-disabled-bg)!important}.bg-disabled-bg{background:var(--sd-disabled-bg)!important}.border-disabled-bg{border-color:var(--sd-disabled-bg)!important}.text-disabled-text{color:var(--sd-disabled-text)!important}.bg-disabled-text{background:var(--sd-disabled-text)!important}.border-disabled-text{border-color:var(--sd-disabled-text)!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}.text-secondary{color:var(--sd-text-secondary)!important}.text-muted{color:var(--sd-text-muted)!important}.text-body{color:var(--sd-text)!important}.bg-muted{background:var(--sd-surface-muted)!important}:host(.sd-bare){position:relative}.sd-inline-view{cursor:pointer;display:inline-flex;align-items:center;width:100%;padding:1px 6px;border-radius:4px;background-color:#0000000a;transition:background-color .15s ease}.sd-inline-view:hover{background-color:#00000014}.sd-inline-view .sd-inline-clear{display:none;align-items:center;margin-left:4px;padding:0;border:0;background:transparent;cursor:pointer;line-height:0;color:var(--sd-text-secondary, #6b7280);transition:color .15s ease}.sd-inline-view .sd-inline-clear sd-icon{font-size:16px;width:16px;height:16px}.sd-inline-view .sd-inline-clear:hover{color:var(--sd-error, #b32626)}.sd-inline-view:hover .sd-inline-clear{display:inline-flex}.sd-inline-editor{position:absolute;inset:0;opacity:0;pointer-events:none}.sd-inline-editor ::ng-deep *{pointer-events:none!important}:host{display:block}:host(.sd-has-label){padding-top:4px}:host(.sd-viewed){padding-top:0}:host ::ng-deep .mat-mdc-form-field.mat-form-field-appearance-outline.mat-form-field-disabled .mat-mdc-text-field-wrapper{background:var(--sd-surface-muted)}:host ::ng-deep .mat-mdc-form-field input.c-selected::placeholder{color:#000;opacity:1}:host ::ng-deep .mat-mdc-form-field input.c-selected:-ms-input-placeholder{color:#000}:host ::ng-deep .mat-mdc-form-field input.c-selected::-ms-input-placeholder{color:#000}:host ::ng-deep .mat-mdc-form-field input.mat-mdc-input-element:disabled{color:var(--sd-disabled-text)!important}:host ::ng-deep .mat-mdc-form-field input.mat-mdc-input-element:disabled.c-selected::placeholder{color:var(--sd-disabled-text)!important}:host ::ng-deep .mat-mdc-form-field input.mat-mdc-input-element:disabled.c-selected:-ms-input-placeholder{color:var(--sd-disabled-text)!important}:host ::ng-deep .mat-mdc-form-field input.mat-mdc-input-element:disabled.c-selected::-ms-input-placeholder{color:var(--sd-disabled-text)!important}:host ::ng-deep .mat-mdc-form-field .mat-mdc-placeholder-required{color:var(--sd-error)}:host ::ng-deep .mat-mdc-form-field:hover .icon-copy{opacity:1}:host ::ng-deep .mat-mdc-form-field .icon-copy{cursor:pointer;width:.9em;height:.9em;fill:#00000080;transition:opacity .2s linear;opacity:0}::ng-deep .sd-autocomplete-panel .mat-mdc-option{min-height:36px!important;padding:8px 12px!important}::ng-deep .sd-autocomplete-panel .mdc-list-item__primary-text{font-size:14px!important;line-height:normal}::ng-deep .sd-autocomplete-panel .mat-pseudo-checkbox{transform:scale(.7);margin-right:8px!important}::ng-deep .sd-autocomplete-panel .mat-pseudo-checkbox-minimal{transform:scale(.7);transform-origin:center right}.sd__option--add{position:sticky;bottom:0;background-color:#fff;z-index:10;color:#000000de;cursor:pointer!important}.sd__option--add-btn{display:flex;align-items:center;width:100%;border:0;padding:0;background:transparent;font:inherit;color:inherit;text-align:left;cursor:pointer}.c-loading-icon{position:absolute;right:5px;top:5px}\n"] }]
463
474
  }], ctorParameters: () => [], propDecorators: { inputRef: [{ type: i0.ViewChild, args: ['input', { isSignal: true }] }], autocompleteTrigger: [{ type: i0.ViewChild, args: [i0.forwardRef(() => MatAutocompleteTrigger), { isSignal: true }] }], sdLabelTemplate: [{ type: i0.ContentChild, args: ['sdLabel', { isSignal: true }] }], sdValueTemplate: [{ type: i0.ContentChild, args: ['sdValue', { isSignal: true }] }], itemDef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SdItemDefDefDirective), { isSignal: true }] }], sdViewDef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SdViewDefDirective), { isSignal: true }] }], autoIdInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoId", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], form: [{ type: i0.Input, args: [{ isSignal: true, alias: "form", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], helperText: [{ type: i0.Input, args: [{ isSignal: true, alias: "helperText", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], valueField: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueField", required: false }] }], displayField: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayField", required: false }] }], disabledField: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabledField", required: false }] }], limit: [{ type: i0.Input, args: [{ isSignal: true, alias: "limit", required: false }] }], cacheChecksum: [{ type: i0.Input, args: [{ isSignal: true, alias: "cacheChecksum", required: false }] }], hyperlink: [{ type: i0.Input, args: [{ isSignal: true, alias: "hyperlink", required: false }] }], items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], hideInlineError: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideInlineError", required: false }] }], addable: [{ type: i0.Input, args: [{ isSignal: true, alias: "addable", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], viewed: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewed", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], validator: [{ type: i0.Input, args: [{ isSignal: true, alias: "validator", required: false }] }], inlineError: [{ type: i0.Input, args: [{ isSignal: true, alias: "inlineError", required: false }] }], appearanceInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", required: false }] }], valueModel: [{ type: i0.Input, args: [{ isSignal: true, alias: "model", required: false }] }, { type: i0.Output, args: ["modelChange"] }], sdChange: [{ type: i0.Output, args: ["sdChange"] }], sdSelection: [{ type: i0.Output, args: ["sdSelection"] }], sdAdd: [{
464
475
  type: Output
465
476
  }] } });