@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
@@ -1,6 +1,6 @@
1
1
  import { CommonModule } from '@angular/common';
2
2
  import * as i0 from '@angular/core';
3
- import { input, output, signal, computed, effect, ChangeDetectionStrategy, Component, booleanAttribute, model, viewChild, isSignal, inject, Injector, viewChildren, afterNextRender, untracked } from '@angular/core';
3
+ import { inject, computed, input, output, signal, effect, ChangeDetectionStrategy, Component, booleanAttribute, model, viewChild, isSignal, Injector, viewChildren, afterNextRender, untracked } from '@angular/core';
4
4
  import * as i1$1 from '@angular/forms';
5
5
  import { FormsModule } from '@angular/forms';
6
6
  import * as i1 from '@angular/material/menu';
@@ -9,6 +9,7 @@ import * as i2 from '@angular/material/tooltip';
9
9
  import { MatTooltipModule } from '@angular/material/tooltip';
10
10
  import { I18nService } from '@sdcorejs/angular/i18n';
11
11
  import { SdIcon } from '@sdcorejs/angular/modules/icon';
12
+ import { SdConfirmService } from '@sdcorejs/angular/services/confirm';
12
13
  import { SdOperator } from '@sdcorejs/angular/components/operator';
13
14
  import { SdDate } from '@sdcorejs/angular/forms/date';
14
15
  import { SdDateRange } from '@sdcorejs/angular/forms/date-range';
@@ -36,6 +37,13 @@ import { SdButton } from '@sdcorejs/angular/components/button';
36
37
  * own "Lưu bộ lọc" button anywhere — typically next to the Search trigger.
37
38
  */
38
39
  class SdQuerySavedFiltersMenu {
40
+ #i18n = inject(I18nService);
41
+ #confirm = inject(SdConfirmService);
42
+ tooltipLabel = computed(() => this.#i18n.t('core.component.query-bar.saved-filters.tooltip'), ...(ngDevMode ? [{ debugName: "tooltipLabel" }] : /* istanbul ignore next */ []));
43
+ tooltipDisabledLabel = computed(() => this.#i18n.t('core.component.query-bar.saved-filters.tooltip-disabled'), ...(ngDevMode ? [{ debugName: "tooltipDisabledLabel" }] : /* istanbul ignore next */ []));
44
+ deleteLabel = computed(() => this.#i18n.t('core.component.query-bar.saved-filters.delete'), ...(ngDevMode ? [{ debugName: "deleteLabel" }] : /* istanbul ignore next */ []));
45
+ emptyLabel = computed(() => this.#i18n.t('core.component.query-bar.saved-filters.empty'), ...(ngDevMode ? [{ debugName: "emptyLabel" }] : /* istanbul ignore next */ []));
46
+ saveLabel = computed(() => this.#i18n.t('core.component.query-bar.saved-filters.save'), ...(ngDevMode ? [{ debugName: "saveLabel" }] : /* istanbul ignore next */ []));
39
47
  /** Namespace key used to scope localStorage. `undefined` → menu disabled. */
40
48
  key = input(undefined, ...(ngDevMode ? [{ debugName: "key" }] : /* istanbul ignore next */ []));
41
49
  /** Current query the host wants to snapshot when the user clicks "Save current". */
@@ -78,10 +86,21 @@ class SdQuerySavedFiltersMenu {
78
86
  * saved filter. Public so the parent toolbar can wire its external "save"
79
87
  * button to this method without re-implementing persistence.
80
88
  */
81
- promptSave() {
89
+ async promptSave() {
82
90
  if (!this.#storageKey())
83
91
  return;
84
- const name = window.prompt('Tên bộ lọc:');
92
+ // why: window.prompt chặn UI thread, không style/i18n được và bị vô hiệu trong vài
93
+ // môi trường (iframe sandbox) — dialog withInput dùng chung hạ tầng confirm của lib.
94
+ let name;
95
+ try {
96
+ name = await this.#confirm.withInput(this.#i18n.t('core.component.query-bar.saved-filters.name-label'), {
97
+ title: this.#i18n.t('core.component.query-bar.saved-filters.save'),
98
+ required: true,
99
+ });
100
+ }
101
+ catch {
102
+ return; // user huỷ dialog
103
+ }
85
104
  if (!name?.trim())
86
105
  return;
87
106
  const filter = {
@@ -105,11 +124,11 @@ class SdQuerySavedFiltersMenu {
105
124
  this.#persist(next);
106
125
  }
107
126
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdQuerySavedFiltersMenu, deps: [], target: i0.ɵɵFactoryTarget.Component });
108
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SdQuerySavedFiltersMenu, isStandalone: true, selector: "sd-query-saved-filters-menu", inputs: { key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: false, transformFunction: null }, query: { classPropertyName: "query", publicName: "query", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { apply: "apply" }, ngImport: i0, template: "<button\n type=\"button\"\n class=\"c-saved-filters\"\n [disabled]=\"!key()\"\n [matMenuTriggerFor]=\"menu\"\n [matTooltip]=\"key() ? 'B\u1ED9 l\u1ECDc \u0111\u00E3 l\u01B0u' : 'C\u1EA7n [savedFiltersKey] \u0111\u1EC3 b\u1EADt'\">\n <sd-icon name=\"bookmark_border\"></sd-icon>\n</button>\n\n<mat-menu #menu=\"matMenu\" class=\"c-saved-filters-menu\" xPosition=\"before\" yPosition=\"below\">\n @for (f of savedFilters(); track f.id) {\n <button type=\"button\" mat-menu-item class=\"c-sf-row\" (click)=\"pick(f)\">\n <sd-icon class=\"c-sf-lead\" name=\"filter_alt\"></sd-icon>\n <span class=\"c-sf-name\">{{ f.name }}</span>\n <span\n role=\"button\"\n tabindex=\"0\"\n class=\"c-sf-del\"\n aria-label=\"Xo\u00E1 b\u1ED9 l\u1ECDc\"\n (click)=\"remove(f.id, $event)\"\n (keydown.enter)=\"remove(f.id, $event)\">\n <sd-icon name=\"close\"></sd-icon>\n </span>\n </button>\n } @empty {\n <div class=\"c-sf-empty\">Ch\u01B0a c\u00F3 b\u1ED9 l\u1ECDc n\u00E0o.</div>\n }\n <!-- why: footer save action s\u1ED1ng trong mat-menu \u0111\u1EC3 dropdown gom 1 v\u00F9ng (list +\n save) \u2014 user m\u1EDF dropdown th\u1EA5y ngay c\u1EA3 2 h\u00E0nh \u0111\u1ED9ng, kh\u00F4ng ph\u1EA3i t\u00ECm n\u00FAt l\u01B0u\n \u1EDF ch\u1ED7 kh\u00E1c tr\u00EAn toolbar. -->\n <hr class=\"c-sf-divider\" aria-hidden=\"true\" />\n <button type=\"button\" mat-menu-item class=\"c-sf-save\" [disabled]=\"!key()\" (click)=\"promptSave()\">\n <sd-icon name=\"bookmark_add\"></sd-icon>\n <span>L\u01B0u b\u1ED9 l\u1ECDc hi\u1EC7n t\u1EA1i</span>\n </button>\n</mat-menu>\n", styles: [":host{display:inline-flex}.c-saved-filters{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border:1px solid #d6d8db;border-radius:6px;background:#fff;color:#5f6368;cursor:pointer;padding:0}.c-saved-filters sd-icon{font-size:18px;width:18px;height:18px}.c-saved-filters:hover:not([disabled]){border-color:var(--sd-primary);color:var(--sd-primary)}.c-saved-filters[disabled]{opacity:.5;cursor:not-allowed}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu{min-width:220px;max-width:320px}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .mat-mdc-menu-item.c-sf-row{min-height:40px;padding-left:16px;padding-right:4px}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .mat-mdc-menu-item.c-sf-row .mat-mdc-menu-item-text{display:flex;align-items:center;gap:8px;min-width:0;line-height:1.4}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-lead{display:inline-flex;align-items:center;justify-content:center;font-size:18px!important;width:18px!important;height:18px!important;line-height:1}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-name{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-del{flex:0 0 auto;margin-left:auto;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:50%;color:#7a7a7a;cursor:pointer;padding:0}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-del sd-icon{margin:0!important;padding:0;font-size:16px!important;width:16px!important;height:16px!important;line-height:16px!important;text-align:center;overflow:hidden}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-del sd-icon mat-icon{margin:0!important}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-del:hover{background:#0000000f;color:#d23535}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-empty{padding:12px 16px;font-size:13px;color:#7a7a7a}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-divider{border:0;border-top:1px solid #e2e4e7;margin:4px 0}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-save sd-icon{margin-right:8px}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i1.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i1.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
127
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SdQuerySavedFiltersMenu, isStandalone: true, selector: "sd-query-saved-filters-menu", inputs: { key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: false, transformFunction: null }, query: { classPropertyName: "query", publicName: "query", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { apply: "apply" }, ngImport: i0, template: "<button\n type=\"button\"\n class=\"c-saved-filters\"\n [disabled]=\"!key()\"\n [matMenuTriggerFor]=\"menu\"\n [matTooltip]=\"key() ? tooltipLabel() : tooltipDisabledLabel()\">\n <sd-icon name=\"bookmark_border\"></sd-icon>\n</button>\n\n<mat-menu #menu=\"matMenu\" class=\"c-saved-filters-menu\" xPosition=\"before\" yPosition=\"below\">\n @for (f of savedFilters(); track f.id) {\n <button type=\"button\" mat-menu-item class=\"c-sf-row\" (click)=\"pick(f)\">\n <sd-icon class=\"c-sf-lead\" name=\"filter_alt\"></sd-icon>\n <span class=\"c-sf-name\">{{ f.name }}</span>\n <span\n role=\"button\"\n tabindex=\"0\"\n class=\"c-sf-del\"\n [attr.aria-label]=\"deleteLabel()\"\n (click)=\"remove(f.id, $event)\"\n (keydown.enter)=\"remove(f.id, $event)\">\n <sd-icon name=\"close\"></sd-icon>\n </span>\n </button>\n } @empty {\n <div class=\"c-sf-empty\">{{ emptyLabel() }}</div>\n }\n <!-- why: footer save action s\u1ED1ng trong mat-menu \u0111\u1EC3 dropdown gom 1 v\u00F9ng (list +\n save) \u2014 user m\u1EDF dropdown th\u1EA5y ngay c\u1EA3 2 h\u00E0nh \u0111\u1ED9ng, kh\u00F4ng ph\u1EA3i t\u00ECm n\u00FAt l\u01B0u\n \u1EDF ch\u1ED7 kh\u00E1c tr\u00EAn toolbar. -->\n <hr class=\"c-sf-divider\" aria-hidden=\"true\" />\n <button type=\"button\" mat-menu-item class=\"c-sf-save\" [disabled]=\"!key()\" (click)=\"promptSave()\">\n <sd-icon name=\"bookmark_add\"></sd-icon>\n <span>{{ saveLabel() }}</span>\n </button>\n</mat-menu>\n", styles: [":host{display:inline-flex}.c-saved-filters{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border:1px solid #d6d8db;border-radius:6px;background:#fff;color:#5f6368;cursor:pointer;padding:0}.c-saved-filters sd-icon{font-size:18px;width:18px;height:18px}.c-saved-filters:hover:not([disabled]){border-color:var(--sd-primary);color:var(--sd-primary)}.c-saved-filters[disabled]{opacity:.5;cursor:not-allowed}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu{min-width:220px;max-width:320px}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .mat-mdc-menu-item.c-sf-row{min-height:40px;padding-left:16px;padding-right:4px}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .mat-mdc-menu-item.c-sf-row .mat-mdc-menu-item-text{display:flex;align-items:center;gap:8px;min-width:0;line-height:1.4}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-lead{display:inline-flex;align-items:center;justify-content:center;margin-right:0;font-size:18px!important;width:18px!important;height:18px!important;line-height:1}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-name{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-del{flex:0 0 auto;margin-left:auto;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:50%;color:#7a7a7a;cursor:pointer;padding:0}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-del sd-icon{margin:0!important;padding:0;font-size:16px!important;width:16px!important;height:16px!important;line-height:16px!important;text-align:center;overflow:hidden}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-del sd-icon mat-icon{margin:0!important}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-del:hover{background:#0000000f;color:#d23535}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-empty{padding:12px 16px;font-size:13px;color:#7a7a7a}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-divider{border:0;border-top:1px solid #e2e4e7;margin:4px 0}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-save sd-icon{margin-right:8px}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i1.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i1.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
109
128
  }
110
129
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdQuerySavedFiltersMenu, decorators: [{
111
130
  type: Component,
112
- args: [{ selector: 'sd-query-saved-filters-menu', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [SdIcon, MatMenuModule, MatTooltipModule], template: "<button\n type=\"button\"\n class=\"c-saved-filters\"\n [disabled]=\"!key()\"\n [matMenuTriggerFor]=\"menu\"\n [matTooltip]=\"key() ? 'B\u1ED9 l\u1ECDc \u0111\u00E3 l\u01B0u' : 'C\u1EA7n [savedFiltersKey] \u0111\u1EC3 b\u1EADt'\">\n <sd-icon name=\"bookmark_border\"></sd-icon>\n</button>\n\n<mat-menu #menu=\"matMenu\" class=\"c-saved-filters-menu\" xPosition=\"before\" yPosition=\"below\">\n @for (f of savedFilters(); track f.id) {\n <button type=\"button\" mat-menu-item class=\"c-sf-row\" (click)=\"pick(f)\">\n <sd-icon class=\"c-sf-lead\" name=\"filter_alt\"></sd-icon>\n <span class=\"c-sf-name\">{{ f.name }}</span>\n <span\n role=\"button\"\n tabindex=\"0\"\n class=\"c-sf-del\"\n aria-label=\"Xo\u00E1 b\u1ED9 l\u1ECDc\"\n (click)=\"remove(f.id, $event)\"\n (keydown.enter)=\"remove(f.id, $event)\">\n <sd-icon name=\"close\"></sd-icon>\n </span>\n </button>\n } @empty {\n <div class=\"c-sf-empty\">Ch\u01B0a c\u00F3 b\u1ED9 l\u1ECDc n\u00E0o.</div>\n }\n <!-- why: footer save action s\u1ED1ng trong mat-menu \u0111\u1EC3 dropdown gom 1 v\u00F9ng (list +\n save) \u2014 user m\u1EDF dropdown th\u1EA5y ngay c\u1EA3 2 h\u00E0nh \u0111\u1ED9ng, kh\u00F4ng ph\u1EA3i t\u00ECm n\u00FAt l\u01B0u\n \u1EDF ch\u1ED7 kh\u00E1c tr\u00EAn toolbar. -->\n <hr class=\"c-sf-divider\" aria-hidden=\"true\" />\n <button type=\"button\" mat-menu-item class=\"c-sf-save\" [disabled]=\"!key()\" (click)=\"promptSave()\">\n <sd-icon name=\"bookmark_add\"></sd-icon>\n <span>L\u01B0u b\u1ED9 l\u1ECDc hi\u1EC7n t\u1EA1i</span>\n </button>\n</mat-menu>\n", styles: [":host{display:inline-flex}.c-saved-filters{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border:1px solid #d6d8db;border-radius:6px;background:#fff;color:#5f6368;cursor:pointer;padding:0}.c-saved-filters sd-icon{font-size:18px;width:18px;height:18px}.c-saved-filters:hover:not([disabled]){border-color:var(--sd-primary);color:var(--sd-primary)}.c-saved-filters[disabled]{opacity:.5;cursor:not-allowed}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu{min-width:220px;max-width:320px}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .mat-mdc-menu-item.c-sf-row{min-height:40px;padding-left:16px;padding-right:4px}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .mat-mdc-menu-item.c-sf-row .mat-mdc-menu-item-text{display:flex;align-items:center;gap:8px;min-width:0;line-height:1.4}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-lead{display:inline-flex;align-items:center;justify-content:center;font-size:18px!important;width:18px!important;height:18px!important;line-height:1}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-name{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-del{flex:0 0 auto;margin-left:auto;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:50%;color:#7a7a7a;cursor:pointer;padding:0}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-del sd-icon{margin:0!important;padding:0;font-size:16px!important;width:16px!important;height:16px!important;line-height:16px!important;text-align:center;overflow:hidden}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-del sd-icon mat-icon{margin:0!important}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-del:hover{background:#0000000f;color:#d23535}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-empty{padding:12px 16px;font-size:13px;color:#7a7a7a}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-divider{border:0;border-top:1px solid #e2e4e7;margin:4px 0}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-save sd-icon{margin-right:8px}\n"] }]
131
+ args: [{ selector: 'sd-query-saved-filters-menu', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [SdIcon, MatMenuModule, MatTooltipModule], template: "<button\n type=\"button\"\n class=\"c-saved-filters\"\n [disabled]=\"!key()\"\n [matMenuTriggerFor]=\"menu\"\n [matTooltip]=\"key() ? tooltipLabel() : tooltipDisabledLabel()\">\n <sd-icon name=\"bookmark_border\"></sd-icon>\n</button>\n\n<mat-menu #menu=\"matMenu\" class=\"c-saved-filters-menu\" xPosition=\"before\" yPosition=\"below\">\n @for (f of savedFilters(); track f.id) {\n <button type=\"button\" mat-menu-item class=\"c-sf-row\" (click)=\"pick(f)\">\n <sd-icon class=\"c-sf-lead\" name=\"filter_alt\"></sd-icon>\n <span class=\"c-sf-name\">{{ f.name }}</span>\n <span\n role=\"button\"\n tabindex=\"0\"\n class=\"c-sf-del\"\n [attr.aria-label]=\"deleteLabel()\"\n (click)=\"remove(f.id, $event)\"\n (keydown.enter)=\"remove(f.id, $event)\">\n <sd-icon name=\"close\"></sd-icon>\n </span>\n </button>\n } @empty {\n <div class=\"c-sf-empty\">{{ emptyLabel() }}</div>\n }\n <!-- why: footer save action s\u1ED1ng trong mat-menu \u0111\u1EC3 dropdown gom 1 v\u00F9ng (list +\n save) \u2014 user m\u1EDF dropdown th\u1EA5y ngay c\u1EA3 2 h\u00E0nh \u0111\u1ED9ng, kh\u00F4ng ph\u1EA3i t\u00ECm n\u00FAt l\u01B0u\n \u1EDF ch\u1ED7 kh\u00E1c tr\u00EAn toolbar. -->\n <hr class=\"c-sf-divider\" aria-hidden=\"true\" />\n <button type=\"button\" mat-menu-item class=\"c-sf-save\" [disabled]=\"!key()\" (click)=\"promptSave()\">\n <sd-icon name=\"bookmark_add\"></sd-icon>\n <span>{{ saveLabel() }}</span>\n </button>\n</mat-menu>\n", styles: [":host{display:inline-flex}.c-saved-filters{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border:1px solid #d6d8db;border-radius:6px;background:#fff;color:#5f6368;cursor:pointer;padding:0}.c-saved-filters sd-icon{font-size:18px;width:18px;height:18px}.c-saved-filters:hover:not([disabled]){border-color:var(--sd-primary);color:var(--sd-primary)}.c-saved-filters[disabled]{opacity:.5;cursor:not-allowed}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu{min-width:220px;max-width:320px}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .mat-mdc-menu-item.c-sf-row{min-height:40px;padding-left:16px;padding-right:4px}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .mat-mdc-menu-item.c-sf-row .mat-mdc-menu-item-text{display:flex;align-items:center;gap:8px;min-width:0;line-height:1.4}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-lead{display:inline-flex;align-items:center;justify-content:center;margin-right:0;font-size:18px!important;width:18px!important;height:18px!important;line-height:1}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-name{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-del{flex:0 0 auto;margin-left:auto;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:50%;color:#7a7a7a;cursor:pointer;padding:0}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-del sd-icon{margin:0!important;padding:0;font-size:16px!important;width:16px!important;height:16px!important;line-height:16px!important;text-align:center;overflow:hidden}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-del sd-icon mat-icon{margin:0!important}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-del:hover{background:#0000000f;color:#d23535}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-empty{padding:12px 16px;font-size:13px;color:#7a7a7a}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-divider{border:0;border-top:1px solid #e2e4e7;margin:4px 0}::ng-deep .mat-mdc-menu-panel.c-saved-filters-menu .c-sf-save sd-icon{margin-right:8px}\n"] }]
113
132
  }], propDecorators: { key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: false }] }], query: [{ type: i0.Input, args: [{ isSignal: true, alias: "query", required: false }] }], apply: [{ type: i0.Output, args: ["apply"] }] } });
114
133
 
115
134
  /**
@@ -123,6 +142,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
123
142
  * immediately before Search so the user sees one filter-cluster on the right.
124
143
  */
125
144
  class SdQueryActionsBar {
145
+ #i18n = inject(I18nService);
126
146
  // AND/OR toggle —
127
147
  showLogicToggle = input(false, { ...(ngDevMode ? { debugName: "showLogicToggle" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
128
148
  logic = model('AND', ...(ngDevMode ? [{ debugName: "logic" }] : /* istanbul ignore next */ []));
@@ -138,17 +158,24 @@ class SdQueryActionsBar {
138
158
  // Search trigger —
139
159
  canSearch = input(false, ...(ngDevMode ? [{ debugName: "canSearch" }] : /* istanbul ignore next */ []));
140
160
  search = output();
161
+ // Nhãn i18n —
162
+ // why: bọc trong `computed()` chứ không dùng pipe `translate` (pure pipe không chạy lại khi
163
+ // `I18nService.setLanguage()` đổi catalog), và `clear-all` còn phải nội suy `{count}` nên
164
+ // ghép chuỗi trong template sẽ khoá cứng trật tự từ của tiếng Việt.
165
+ logicGroupLabel = computed(() => this.#i18n.t('core.component.query-bar.logic-operator'), ...(ngDevMode ? [{ debugName: "logicGroupLabel" }] : /* istanbul ignore next */ []));
166
+ clearAllLabel = computed(() => this.#i18n.t('core.component.query-bar.clear-all', { count: this.filtersCount() }), ...(ngDevMode ? [{ debugName: "clearAllLabel" }] : /* istanbul ignore next */ []));
167
+ searchLabel = computed(() => this.#i18n.t('core.component.query-bar.search'), ...(ngDevMode ? [{ debugName: "searchLabel" }] : /* istanbul ignore next */ []));
141
168
  setLogic(value) {
142
169
  if (this.logic() === value)
143
170
  return;
144
171
  this.logic.set(value);
145
172
  }
146
173
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdQueryActionsBar, deps: [], target: i0.ɵɵFactoryTarget.Component });
147
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SdQueryActionsBar, isStandalone: true, selector: "sd-query-actions-bar", inputs: { showLogicToggle: { classPropertyName: "showLogicToggle", publicName: "showLogicToggle", isSignal: true, isRequired: false, transformFunction: null }, logic: { classPropertyName: "logic", publicName: "logic", isSignal: true, isRequired: false, transformFunction: null }, filtersCount: { classPropertyName: "filtersCount", publicName: "filtersCount", isSignal: true, isRequired: false, transformFunction: null }, showSavedFilters: { classPropertyName: "showSavedFilters", publicName: "showSavedFilters", isSignal: true, isRequired: false, transformFunction: null }, savedFiltersKey: { classPropertyName: "savedFiltersKey", publicName: "savedFiltersKey", isSignal: true, isRequired: false, transformFunction: null }, query: { classPropertyName: "query", publicName: "query", isSignal: true, isRequired: false, transformFunction: null }, showClearAll: { classPropertyName: "showClearAll", publicName: "showClearAll", isSignal: true, isRequired: false, transformFunction: null }, canSearch: { classPropertyName: "canSearch", publicName: "canSearch", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { logic: "logicChange", applyFilter: "applyFilter", clear: "clear", search: "search" }, ngImport: i0, template: "<!-- Vertical separator between the wrapping filter area (left) and the pinned toolbar. -->\n<span class=\"c-bar-divider\" aria-hidden=\"true\"></span>\n\n<div class=\"c-query-bar__actions\">\n <!-- AND / OR toggle -->\n @if (showLogicToggle() && filtersCount() >= 2) {\n <div class=\"c-logic-toggle\" role=\"group\" aria-label=\"Logic operator\">\n <button\n type=\"button\"\n class=\"c-logic-segment\"\n [class.c-logic-active]=\"logic() === 'AND'\"\n (click)=\"setLogic('AND')\">\n AND\n </button>\n <button\n type=\"button\"\n class=\"c-logic-segment\"\n [class.c-logic-active]=\"logic() === 'OR'\"\n (click)=\"setLogic('OR')\">\n OR\n </button>\n </div>\n }\n\n @if (showClearAll() && filtersCount() > 0) {\n <button\n type=\"button\"\n class=\"c-clear-all\"\n [matTooltip]=\"'X\u00F3a t\u1EA5t c\u1EA3 (' + filtersCount() + ')'\"\n (click)=\"clear.emit()\">\n <sd-icon name=\"delete_sweep\"></sd-icon>\n </button>\n }\n\n <!-- why: saved-filters dropdown \u0111\u1EB7t s\u00E1t ngay tr\u01B0\u1EDBc Search \u2014 gom to\u00E0n b\u1ED9\n h\u00E0nh \u0111\u1ED9ng v\u1EC1 b\u1ED9 l\u1ECDc (apply view c\u0169 / l\u01B0u view hi\u1EC7n t\u1EA1i) c\u1EA1nh n\u00FAt submit,\n user th\u1EA5y 1 c\u1EE5m. Footer \"L\u01B0u b\u1ED9 l\u1ECDc hi\u1EC7n t\u1EA1i\" s\u1ED1ng trong mat-menu c\u1EE7a\n saved-filters-menu, kh\u00F4ng t\u00E1ch ra n\u00FAt ri\u00EAng n\u1EEFa. -->\n @if (showSavedFilters()) {\n <sd-query-saved-filters-menu\n [key]=\"savedFiltersKey()\"\n [query]=\"query()\"\n (apply)=\"applyFilter.emit($event)\" />\n }\n\n <!-- why: far-right \"submit at the end of the toolbar\" position; sole query trigger. -->\n <button\n type=\"button\"\n class=\"c-search-trigger\"\n [disabled]=\"!canSearch()\"\n matTooltip=\"T\u00ECm ki\u1EBFm\"\n (click)=\"search.emit()\">\n <sd-icon name=\"search\"></sd-icon>\n </button>\n</div>\n", styles: [":host{display:contents}.c-bar-divider{flex:0 0 auto;align-self:stretch;width:1px;min-height:28px;background:#e2e4e7}.c-query-bar__actions{flex:0 0 auto;display:flex;align-items:center;gap:6px}.c-logic-toggle{display:inline-flex;align-items:center;padding:2px;background:#f5f6f7;border:1px solid #e2e4e7;border-radius:6px}:host-context(.c-density-compact) .c-logic-toggle{height:28px}:host-context(.c-density-comfortable) .c-logic-toggle{height:32px}.c-logic-toggle .c-logic-segment{height:100%;padding:0 10px;border:none;background:transparent;font-family:Roboto Mono,monospace;font-size:11px;font-weight:600;letter-spacing:.4px;color:#5f6368;cursor:pointer;border-radius:4px}.c-logic-toggle .c-logic-segment.c-logic-active{background:#fff;color:var(--sd-primary);box-shadow:0 1px 2px #00000014}.c-clear-all,.c-search-trigger{display:inline-flex;align-items:center;justify-content:center;border:1px solid #d6d8db;border-radius:6px;background:#fff;color:#5f6368;cursor:pointer;padding:0}:host-context(.c-density-compact) .c-clear-all,:host-context(.c-density-compact) .c-search-trigger{width:28px;height:28px}:host-context(.c-density-comfortable) .c-clear-all,:host-context(.c-density-comfortable) .c-search-trigger{width:32px;height:32px}.c-clear-all sd-icon,.c-search-trigger sd-icon{font-size:18px;width:18px;height:18px}.c-clear-all:hover:not([disabled]),.c-search-trigger:hover:not([disabled]){border-color:var(--sd-primary);color:var(--sd-primary)}.c-clear-all[disabled],.c-search-trigger[disabled]{opacity:.5;cursor:not-allowed}.c-clear-all:hover{border-color:#dc2626;color:#dc2626}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: SdQuerySavedFiltersMenu, selector: "sd-query-saved-filters-menu", inputs: ["key", "query"], outputs: ["apply"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
174
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SdQueryActionsBar, isStandalone: true, selector: "sd-query-actions-bar", inputs: { showLogicToggle: { classPropertyName: "showLogicToggle", publicName: "showLogicToggle", isSignal: true, isRequired: false, transformFunction: null }, logic: { classPropertyName: "logic", publicName: "logic", isSignal: true, isRequired: false, transformFunction: null }, filtersCount: { classPropertyName: "filtersCount", publicName: "filtersCount", isSignal: true, isRequired: false, transformFunction: null }, showSavedFilters: { classPropertyName: "showSavedFilters", publicName: "showSavedFilters", isSignal: true, isRequired: false, transformFunction: null }, savedFiltersKey: { classPropertyName: "savedFiltersKey", publicName: "savedFiltersKey", isSignal: true, isRequired: false, transformFunction: null }, query: { classPropertyName: "query", publicName: "query", isSignal: true, isRequired: false, transformFunction: null }, showClearAll: { classPropertyName: "showClearAll", publicName: "showClearAll", isSignal: true, isRequired: false, transformFunction: null }, canSearch: { classPropertyName: "canSearch", publicName: "canSearch", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { logic: "logicChange", applyFilter: "applyFilter", clear: "clear", search: "search" }, ngImport: i0, template: "<!-- Vertical separator between the wrapping filter area (left) and the pinned toolbar. -->\n<span class=\"c-bar-divider\" aria-hidden=\"true\"></span>\n\n<div class=\"c-query-bar__actions\">\n <!-- AND / OR toggle -->\n @if (showLogicToggle() && filtersCount() >= 2) {\n <div class=\"c-logic-toggle\" role=\"group\" [attr.aria-label]=\"logicGroupLabel()\">\n <button\n type=\"button\"\n class=\"c-logic-segment\"\n [class.c-logic-active]=\"logic() === 'AND'\"\n (click)=\"setLogic('AND')\">\n AND\n </button>\n <button\n type=\"button\"\n class=\"c-logic-segment\"\n [class.c-logic-active]=\"logic() === 'OR'\"\n (click)=\"setLogic('OR')\">\n OR\n </button>\n </div>\n }\n\n @if (showClearAll() && filtersCount() > 0) {\n <button\n type=\"button\"\n class=\"c-clear-all\"\n [matTooltip]=\"clearAllLabel()\"\n (click)=\"clear.emit()\">\n <sd-icon name=\"delete_sweep\"></sd-icon>\n </button>\n }\n\n <!-- why: saved-filters dropdown \u0111\u1EB7t s\u00E1t ngay tr\u01B0\u1EDBc Search \u2014 gom to\u00E0n b\u1ED9\n h\u00E0nh \u0111\u1ED9ng v\u1EC1 b\u1ED9 l\u1ECDc (apply view c\u0169 / l\u01B0u view hi\u1EC7n t\u1EA1i) c\u1EA1nh n\u00FAt submit,\n user th\u1EA5y 1 c\u1EE5m. Footer \"L\u01B0u b\u1ED9 l\u1ECDc hi\u1EC7n t\u1EA1i\" s\u1ED1ng trong mat-menu c\u1EE7a\n saved-filters-menu, kh\u00F4ng t\u00E1ch ra n\u00FAt ri\u00EAng n\u1EEFa. -->\n @if (showSavedFilters()) {\n <sd-query-saved-filters-menu\n [key]=\"savedFiltersKey()\"\n [query]=\"query()\"\n (apply)=\"applyFilter.emit($event)\" />\n }\n\n <!-- why: far-right \"submit at the end of the toolbar\" position; sole query trigger. -->\n <button\n type=\"button\"\n class=\"c-search-trigger\"\n [disabled]=\"!canSearch()\"\n [matTooltip]=\"searchLabel()\"\n (click)=\"search.emit()\">\n <sd-icon name=\"search\"></sd-icon>\n </button>\n</div>\n", styles: [":host{display:contents}.c-bar-divider{flex:0 0 auto;align-self:stretch;width:1px;min-height:28px;background:#e2e4e7}.c-query-bar__actions{flex:0 0 auto;display:flex;align-items:center;gap:6px}.c-logic-toggle{display:inline-flex;align-items:center;padding:2px;background:#f5f6f7;border:1px solid #e2e4e7;border-radius:6px}:host-context(.c-density-compact) .c-logic-toggle{height:28px}:host-context(.c-density-comfortable) .c-logic-toggle{height:32px}.c-logic-toggle .c-logic-segment{height:100%;padding:0 10px;border:none;background:transparent;font-family:Roboto Mono,monospace;font-size:11px;font-weight:600;letter-spacing:.4px;color:#5f6368;cursor:pointer;border-radius:4px}.c-logic-toggle .c-logic-segment.c-logic-active{background:#fff;color:var(--sd-primary);box-shadow:0 1px 2px #00000014}.c-clear-all,.c-search-trigger{display:inline-flex;align-items:center;justify-content:center;border:1px solid #d6d8db;border-radius:6px;background:#fff;color:#5f6368;cursor:pointer;padding:0}:host-context(.c-density-compact) .c-clear-all,:host-context(.c-density-compact) .c-search-trigger{width:28px;height:28px}:host-context(.c-density-comfortable) .c-clear-all,:host-context(.c-density-comfortable) .c-search-trigger{width:32px;height:32px}.c-clear-all sd-icon,.c-search-trigger sd-icon{font-size:18px;width:18px;height:18px}.c-clear-all:hover:not([disabled]),.c-search-trigger:hover:not([disabled]){border-color:var(--sd-primary);color:var(--sd-primary)}.c-clear-all[disabled],.c-search-trigger[disabled]{opacity:.5;cursor:not-allowed}.c-clear-all:hover{border-color:#dc2626;color:#dc2626}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: SdQuerySavedFiltersMenu, selector: "sd-query-saved-filters-menu", inputs: ["key", "query"], outputs: ["apply"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
148
175
  }
149
176
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdQueryActionsBar, decorators: [{
150
177
  type: Component,
151
- args: [{ selector: 'sd-query-actions-bar', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [SdIcon, MatTooltipModule, SdQuerySavedFiltersMenu], template: "<!-- Vertical separator between the wrapping filter area (left) and the pinned toolbar. -->\n<span class=\"c-bar-divider\" aria-hidden=\"true\"></span>\n\n<div class=\"c-query-bar__actions\">\n <!-- AND / OR toggle -->\n @if (showLogicToggle() && filtersCount() >= 2) {\n <div class=\"c-logic-toggle\" role=\"group\" aria-label=\"Logic operator\">\n <button\n type=\"button\"\n class=\"c-logic-segment\"\n [class.c-logic-active]=\"logic() === 'AND'\"\n (click)=\"setLogic('AND')\">\n AND\n </button>\n <button\n type=\"button\"\n class=\"c-logic-segment\"\n [class.c-logic-active]=\"logic() === 'OR'\"\n (click)=\"setLogic('OR')\">\n OR\n </button>\n </div>\n }\n\n @if (showClearAll() && filtersCount() > 0) {\n <button\n type=\"button\"\n class=\"c-clear-all\"\n [matTooltip]=\"'X\u00F3a t\u1EA5t c\u1EA3 (' + filtersCount() + ')'\"\n (click)=\"clear.emit()\">\n <sd-icon name=\"delete_sweep\"></sd-icon>\n </button>\n }\n\n <!-- why: saved-filters dropdown \u0111\u1EB7t s\u00E1t ngay tr\u01B0\u1EDBc Search \u2014 gom to\u00E0n b\u1ED9\n h\u00E0nh \u0111\u1ED9ng v\u1EC1 b\u1ED9 l\u1ECDc (apply view c\u0169 / l\u01B0u view hi\u1EC7n t\u1EA1i) c\u1EA1nh n\u00FAt submit,\n user th\u1EA5y 1 c\u1EE5m. Footer \"L\u01B0u b\u1ED9 l\u1ECDc hi\u1EC7n t\u1EA1i\" s\u1ED1ng trong mat-menu c\u1EE7a\n saved-filters-menu, kh\u00F4ng t\u00E1ch ra n\u00FAt ri\u00EAng n\u1EEFa. -->\n @if (showSavedFilters()) {\n <sd-query-saved-filters-menu\n [key]=\"savedFiltersKey()\"\n [query]=\"query()\"\n (apply)=\"applyFilter.emit($event)\" />\n }\n\n <!-- why: far-right \"submit at the end of the toolbar\" position; sole query trigger. -->\n <button\n type=\"button\"\n class=\"c-search-trigger\"\n [disabled]=\"!canSearch()\"\n matTooltip=\"T\u00ECm ki\u1EBFm\"\n (click)=\"search.emit()\">\n <sd-icon name=\"search\"></sd-icon>\n </button>\n</div>\n", styles: [":host{display:contents}.c-bar-divider{flex:0 0 auto;align-self:stretch;width:1px;min-height:28px;background:#e2e4e7}.c-query-bar__actions{flex:0 0 auto;display:flex;align-items:center;gap:6px}.c-logic-toggle{display:inline-flex;align-items:center;padding:2px;background:#f5f6f7;border:1px solid #e2e4e7;border-radius:6px}:host-context(.c-density-compact) .c-logic-toggle{height:28px}:host-context(.c-density-comfortable) .c-logic-toggle{height:32px}.c-logic-toggle .c-logic-segment{height:100%;padding:0 10px;border:none;background:transparent;font-family:Roboto Mono,monospace;font-size:11px;font-weight:600;letter-spacing:.4px;color:#5f6368;cursor:pointer;border-radius:4px}.c-logic-toggle .c-logic-segment.c-logic-active{background:#fff;color:var(--sd-primary);box-shadow:0 1px 2px #00000014}.c-clear-all,.c-search-trigger{display:inline-flex;align-items:center;justify-content:center;border:1px solid #d6d8db;border-radius:6px;background:#fff;color:#5f6368;cursor:pointer;padding:0}:host-context(.c-density-compact) .c-clear-all,:host-context(.c-density-compact) .c-search-trigger{width:28px;height:28px}:host-context(.c-density-comfortable) .c-clear-all,:host-context(.c-density-comfortable) .c-search-trigger{width:32px;height:32px}.c-clear-all sd-icon,.c-search-trigger sd-icon{font-size:18px;width:18px;height:18px}.c-clear-all:hover:not([disabled]),.c-search-trigger:hover:not([disabled]){border-color:var(--sd-primary);color:var(--sd-primary)}.c-clear-all[disabled],.c-search-trigger[disabled]{opacity:.5;cursor:not-allowed}.c-clear-all:hover{border-color:#dc2626;color:#dc2626}\n"] }]
178
+ args: [{ selector: 'sd-query-actions-bar', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [SdIcon, MatTooltipModule, SdQuerySavedFiltersMenu], template: "<!-- Vertical separator between the wrapping filter area (left) and the pinned toolbar. -->\n<span class=\"c-bar-divider\" aria-hidden=\"true\"></span>\n\n<div class=\"c-query-bar__actions\">\n <!-- AND / OR toggle -->\n @if (showLogicToggle() && filtersCount() >= 2) {\n <div class=\"c-logic-toggle\" role=\"group\" [attr.aria-label]=\"logicGroupLabel()\">\n <button\n type=\"button\"\n class=\"c-logic-segment\"\n [class.c-logic-active]=\"logic() === 'AND'\"\n (click)=\"setLogic('AND')\">\n AND\n </button>\n <button\n type=\"button\"\n class=\"c-logic-segment\"\n [class.c-logic-active]=\"logic() === 'OR'\"\n (click)=\"setLogic('OR')\">\n OR\n </button>\n </div>\n }\n\n @if (showClearAll() && filtersCount() > 0) {\n <button\n type=\"button\"\n class=\"c-clear-all\"\n [matTooltip]=\"clearAllLabel()\"\n (click)=\"clear.emit()\">\n <sd-icon name=\"delete_sweep\"></sd-icon>\n </button>\n }\n\n <!-- why: saved-filters dropdown \u0111\u1EB7t s\u00E1t ngay tr\u01B0\u1EDBc Search \u2014 gom to\u00E0n b\u1ED9\n h\u00E0nh \u0111\u1ED9ng v\u1EC1 b\u1ED9 l\u1ECDc (apply view c\u0169 / l\u01B0u view hi\u1EC7n t\u1EA1i) c\u1EA1nh n\u00FAt submit,\n user th\u1EA5y 1 c\u1EE5m. Footer \"L\u01B0u b\u1ED9 l\u1ECDc hi\u1EC7n t\u1EA1i\" s\u1ED1ng trong mat-menu c\u1EE7a\n saved-filters-menu, kh\u00F4ng t\u00E1ch ra n\u00FAt ri\u00EAng n\u1EEFa. -->\n @if (showSavedFilters()) {\n <sd-query-saved-filters-menu\n [key]=\"savedFiltersKey()\"\n [query]=\"query()\"\n (apply)=\"applyFilter.emit($event)\" />\n }\n\n <!-- why: far-right \"submit at the end of the toolbar\" position; sole query trigger. -->\n <button\n type=\"button\"\n class=\"c-search-trigger\"\n [disabled]=\"!canSearch()\"\n [matTooltip]=\"searchLabel()\"\n (click)=\"search.emit()\">\n <sd-icon name=\"search\"></sd-icon>\n </button>\n</div>\n", styles: [":host{display:contents}.c-bar-divider{flex:0 0 auto;align-self:stretch;width:1px;min-height:28px;background:#e2e4e7}.c-query-bar__actions{flex:0 0 auto;display:flex;align-items:center;gap:6px}.c-logic-toggle{display:inline-flex;align-items:center;padding:2px;background:#f5f6f7;border:1px solid #e2e4e7;border-radius:6px}:host-context(.c-density-compact) .c-logic-toggle{height:28px}:host-context(.c-density-comfortable) .c-logic-toggle{height:32px}.c-logic-toggle .c-logic-segment{height:100%;padding:0 10px;border:none;background:transparent;font-family:Roboto Mono,monospace;font-size:11px;font-weight:600;letter-spacing:.4px;color:#5f6368;cursor:pointer;border-radius:4px}.c-logic-toggle .c-logic-segment.c-logic-active{background:#fff;color:var(--sd-primary);box-shadow:0 1px 2px #00000014}.c-clear-all,.c-search-trigger{display:inline-flex;align-items:center;justify-content:center;border:1px solid #d6d8db;border-radius:6px;background:#fff;color:#5f6368;cursor:pointer;padding:0}:host-context(.c-density-compact) .c-clear-all,:host-context(.c-density-compact) .c-search-trigger{width:28px;height:28px}:host-context(.c-density-comfortable) .c-clear-all,:host-context(.c-density-comfortable) .c-search-trigger{width:32px;height:32px}.c-clear-all sd-icon,.c-search-trigger sd-icon{font-size:18px;width:18px;height:18px}.c-clear-all:hover:not([disabled]),.c-search-trigger:hover:not([disabled]){border-color:var(--sd-primary);color:var(--sd-primary)}.c-clear-all[disabled],.c-search-trigger[disabled]{opacity:.5;cursor:not-allowed}.c-clear-all:hover{border-color:#dc2626;color:#dc2626}\n"] }]
152
179
  }], propDecorators: { showLogicToggle: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLogicToggle", required: false }] }], logic: [{ type: i0.Input, args: [{ isSignal: true, alias: "logic", required: false }] }, { type: i0.Output, args: ["logicChange"] }], filtersCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "filtersCount", required: false }] }], showSavedFilters: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSavedFilters", required: false }] }], savedFiltersKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "savedFiltersKey", required: false }] }], query: [{ type: i0.Input, args: [{ isSignal: true, alias: "query", required: false }] }], applyFilter: [{ type: i0.Output, args: ["applyFilter"] }], showClearAll: [{ type: i0.Input, args: [{ isSignal: true, alias: "showClearAll", required: false }] }], clear: [{ type: i0.Output, args: ["clear"] }], canSearch: [{ type: i0.Input, args: [{ isSignal: true, alias: "canSearch", required: false }] }], search: [{ type: i0.Output, args: ["search"] }] } });
153
180
 
154
181
  // ---------------------------------------------------------------------------
@@ -201,18 +228,41 @@ function sdQueryDefaultOperator(field) {
201
228
  return field.defaultOperator;
202
229
  return SD_QUERY_DEFAULT_OPERATOR_BY_TYPE[field.type];
203
230
  }
231
+ /**
232
+ * Cache của nhánh "simple mode" trong `sdQueryAllowedOperators`, keyed theo chính object field.
233
+ *
234
+ * why: nhánh mặc định (`field.operators` không khai báo) là case PHỔ BIẾN nhất và trước đây cấp
235
+ * phát một mảng `[defaultOperator]` MỚI mỗi lần gọi. Template bind
236
+ * `[allowedOperators]="allowedOperatorsFor(_b.field)"` nên hàm này chạy lại mỗi chu kỳ change
237
+ * detection → child OnPush nhận input ref mới → markForCheck → CD mới → mảng mới → vòng lặp vô
238
+ * hạn, đúng bug class OOM đã ghi ở `query-builder` `#booleanOptionsByKey`. Dùng WeakMap để
239
+ * không giữ field sống lâu hơn consumer.
240
+ *
241
+ * Chỉ memo hoá nhánh mặc định: hai nhánh còn lại trả về hằng số chung
242
+ * (`SD_QUERY_OPERATORS_BY_TYPE`) hoặc chính mảng của consumer, vốn đã ổn định sẵn — và giữ
243
+ * chúng ngoài cache để field đổi `operators` tại chỗ vẫn được đọc lại.
244
+ */
245
+ const DEFAULT_OPERATOR_LIST_CACHE = new WeakMap();
204
246
  /**
205
247
  * Operators offered in the chip popover for `field`:
206
248
  * - `operators === true` → full set for the type
207
249
  * - `operators` is an array → that array (deduped against the type set is the caller's job)
208
250
  * - otherwise (simple mode) → just the single default operator (no real choice)
251
+ *
252
+ * The returned array is referentially stable for a given `field` object, so it is safe to call
253
+ * straight from a template binding.
209
254
  */
210
255
  function sdQueryAllowedOperators(field) {
211
256
  if (field.operators === true)
212
257
  return SD_QUERY_OPERATORS_BY_TYPE[field.type];
213
258
  if (Array.isArray(field.operators) && field.operators.length > 0)
214
259
  return field.operators;
215
- return [sdQueryDefaultOperator(field)];
260
+ const cached = DEFAULT_OPERATOR_LIST_CACHE.get(field);
261
+ if (cached)
262
+ return cached;
263
+ const single = [sdQueryDefaultOperator(field)];
264
+ DEFAULT_OPERATOR_LIST_CACHE.set(field, single);
265
+ return single;
216
266
  }
217
267
  /**
218
268
  * Whether the operator selector should be visible. Simple mode (operators omitted/false,
@@ -234,6 +284,7 @@ function sdQueryShowOperatorSelector(field) {
234
284
  * query trigger. Enter commits + blurs, Esc reverts + blurs, blur commits.
235
285
  */
236
286
  class SdQueryInlineValueChip {
287
+ #i18n = inject(I18nService);
237
288
  /** Field this chip filters by — must be kind `string` or `number`. */
238
289
  field = input.required(...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
239
290
  /** Active operator (drives BETWEEN dual-input + no-data hiding). */
@@ -262,9 +313,11 @@ class SdQueryInlineValueChip {
262
313
  isBetween = computed(() => this.operator() === 'BETWEEN', ...(ngDevMode ? [{ debugName: "isBetween" }] : /* istanbul ignore next */ []));
263
314
  isNoData = computed(() => SD_QUERY_NO_DATA_OPERATORS.includes(this.operator()), ...(ngDevMode ? [{ debugName: "isNoData" }] : /* istanbul ignore next */ []));
264
315
  icon = computed(() => sdQueryFieldIcon(this.field()), ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
265
- ph = computed(() => (this.isNumber() ? 'giá trị' : 'nhập…'), ...(ngDevMode ? [{ debugName: "ph" }] : /* istanbul ignore next */ []));
266
- phFrom = computed(() => (this.isNumber() ? 'Từ' : 'từ…'), ...(ngDevMode ? [{ debugName: "phFrom" }] : /* istanbul ignore next */ []));
267
- phTo = computed(() => (this.isNumber() ? 'Đến' : 'đến…'), ...(ngDevMode ? [{ debugName: "phTo" }] : /* istanbul ignore next */ []));
316
+ // why: placeholder của field số field chuỗi khác nhau về cách viết hoa/dấu ba chấm nên phải
317
+ // key riêng không tự ghép được từ một chuỗi chung cho mọi ngôn ngữ.
318
+ ph = computed(() => this.#i18n.t(this.isNumber() ? 'core.component.query-bar.placeholder.value' : 'core.component.query-bar.placeholder.text'), ...(ngDevMode ? [{ debugName: "ph" }] : /* istanbul ignore next */ []));
319
+ phFrom = computed(() => this.#i18n.t(this.isNumber() ? 'core.component.query-bar.placeholder.from-number' : 'core.component.query-bar.placeholder.from-text'), ...(ngDevMode ? [{ debugName: "phFrom" }] : /* istanbul ignore next */ []));
320
+ phTo = computed(() => this.#i18n.t(this.isNumber() ? 'core.component.query-bar.placeholder.to-number' : 'core.component.query-bar.placeholder.to-text'), ...(ngDevMode ? [{ debugName: "phTo" }] : /* istanbul ignore next */ []));
268
321
  hasValue = computed(() => {
269
322
  if (this.isNoData())
270
323
  return true;
@@ -378,11 +431,11 @@ class SdQueryInlineValueChip {
378
431
  return { ok: true, value: t };
379
432
  }
380
433
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdQueryInlineValueChip, deps: [], target: i0.ɵɵFactoryTarget.Component });
381
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SdQueryInlineValueChip, isStandalone: true, selector: "sd-query-inline-value-chip", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, operator: { classPropertyName: "operator", publicName: "operator", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, density: { classPropertyName: "density", publicName: "density", isSignal: true, isRequired: false, transformFunction: null }, showOperator: { classPropertyName: "showOperator", publicName: "showOperator", isSignal: true, isRequired: false, transformFunction: null }, autoId: { classPropertyName: "autoId", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, autofocus: { classPropertyName: "autofocus", publicName: "autofocus", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange", remove: "remove" }, viewQueries: [{ propertyName: "firstInput", first: true, predicate: ["firstInput"], descendants: true, isSignal: true }], ngImport: i0, template: "<!--\n Seamless string/number chip \u2014 the pill IS the input. One border, three flush\n segments: label \u00B7 input(s) \u00B7 \u00D7. State styling driven by [data-state]. The value\n field is the shared <sd-inline-text> primitive (chrome=\"seamless\") so width hugs\n content; the chip keeps the envelope + parse/commit logic.\n-->\n@let _state = state();\n@let _autoId = autoId();\n@let _isNoData = isNoData();\n@let _density = density();\n@let _operator = operator();\n\n<span\n class=\"c-seamless\"\n [attr.data-state]=\"_state\"\n [class.c-seamless--compact]=\"_density === 'compact'\"\n [class.c-seamless--comfortable]=\"_density === 'comfortable'\"\n (click)=\"focusFromShell($event)\">\n\n <!-- Label segment: icon + field name (+ optional operator pill) -->\n <span class=\"c-seamless__label\">\n <sd-icon class=\"c-seamless__icon\" [name]=\"icon()\"></sd-icon>\n <span class=\"c-seamless__field\">{{ field().label }}</span>\n @if (showOperator() && !_isNoData) {\n <sd-operator class=\"c-seamless__op\" [operators]=\"[_operator]\" [model]=\"_operator\" [disabled]=\"true\" />\n } @else if (!_isNoData) {\n <span class=\"c-seamless__sep\">:</span>\n }\n </span>\n\n <!-- Input segment \u2014 seamless borderless primitive; hidden for NULL / NOT_NULL -->\n @if (!_isNoData) {\n <span class=\"c-seamless__input\">\n @if (isBetween()) {\n <sd-inline-text\n #firstInput\n chrome=\"seamless\"\n [clearable]=\"false\"\n [autofocus]=\"autofocus()\"\n [state]=\"_state\"\n [value]=\"draftFrom()\"\n (valueChange)=\"draftFrom.set($event)\"\n [placeholder]=\"phFrom()\"\n [autoId]=\"_autoId ? _autoId + '-from' : undefined\"\n (sdFocus)=\"onFocus()\"\n (sdBlur)=\"commitRange()\"\n (keyupEnter)=\"firstInput.blur()\"\n (keydownEscape)=\"revertAndBlur(firstInput)\" />\n <span class=\"c-seamless__dash\" aria-hidden=\"true\">\u2014</span>\n <sd-inline-text\n #secondInput\n chrome=\"seamless\"\n [clearable]=\"false\"\n [state]=\"_state\"\n [value]=\"draftTo()\"\n (valueChange)=\"draftTo.set($event)\"\n [placeholder]=\"phTo()\"\n [autoId]=\"_autoId ? _autoId + '-to' : undefined\"\n (sdFocus)=\"onFocus()\"\n (sdBlur)=\"commitRange()\"\n (keyupEnter)=\"secondInput.blur()\"\n (keydownEscape)=\"revertAndBlur(secondInput)\" />\n } @else {\n <sd-inline-text\n #firstInput\n chrome=\"seamless\"\n [clearable]=\"false\"\n [autofocus]=\"autofocus()\"\n [state]=\"_state\"\n [value]=\"draft()\"\n (valueChange)=\"draft.set($event)\"\n [placeholder]=\"ph()\"\n [autoId]=\"_autoId ?? undefined\"\n (sdFocus)=\"onFocus()\"\n (sdBlur)=\"commitSingle()\"\n (keyupEnter)=\"firstInput.blur()\"\n (keydownEscape)=\"revertAndBlur(firstInput)\" />\n }\n </span>\n }\n\n <!-- \u00D7 segment -->\n <button type=\"button\" class=\"c-seamless__x\" aria-label=\"Remove filter\" (click)=\"onRemove($event)\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n</span>\n", styles: [":host{display:inline-flex}.c-seamless{display:inline-flex;align-items:stretch;border-radius:999px;overflow:hidden;font-family:inherit;font-size:13px;line-height:1;cursor:text;border:1px solid #d6d8db;background:#fff;color:#5f6368;transition:border-color .12s ease,background-color .12s ease,box-shadow .12s ease,color .12s ease}.c-seamless--compact{height:28px}.c-seamless--comfortable{height:32px}.c-seamless:hover:not([data-state=focus]):not([data-state=error]){border-color:var(--sd-primary)}.c-seamless[data-state=pending]{border-style:dashed;border-color:#d6d8db;background:#fff;color:#5f6368}.c-seamless[data-state=active]{border-color:var(--sd-primary-light);background:var(--sd-primary-light)}.c-seamless[data-state=focus]{border-color:var(--sd-primary);background:#fff;box-shadow:0 0 0 3px var(--sd-primary-light)}.c-seamless[data-state=error]{border-color:#d23535;background:#fff5f5;color:#d23535;box-shadow:0 0 0 3px #d235351f}.c-seamless__label{display:inline-flex;align-items:center;gap:4px;font-weight:500;white-space:nowrap;cursor:text;color:inherit}.c-seamless--compact .c-seamless__label{padding:0 0 0 12px}.c-seamless--comfortable .c-seamless__label{padding:0 0 0 14px}.c-seamless__icon{font-size:14px;width:14px;height:14px;color:inherit}.c-seamless__field{color:inherit}.c-seamless__op{display:inline-flex;align-items:center;color:inherit}.c-seamless__sep{color:#7a7a7a}.c-seamless__input{display:inline-flex;align-items:center;gap:4px}.c-seamless--compact .c-seamless__input{padding:0 8px 0 4px}.c-seamless--comfortable .c-seamless__input{padding:0 10px 0 6px}.c-seamless__dash{color:#7a7a7a}.c-seamless__x{display:inline-flex;align-items:center;justify-content:center;border:0;border-left:1px solid rgba(0,0,0,.04);background:transparent;cursor:pointer;color:inherit}.c-seamless--compact .c-seamless__x{width:26px}.c-seamless--comfortable .c-seamless__x{width:28px}.c-seamless__x sd-icon{font-size:14px;width:14px;height:14px}.c-seamless__x:hover{background:#0000000d}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "component", type: SdOperator, selector: "sd-operator", inputs: ["model", "operators", "disabled", "autoId"], outputs: ["modelChange"] }, { kind: "component", type: SdInlineText, selector: "sd-inline-text", inputs: ["value", "control", "placeholder", "disabled", "clearable", "state", "chrome", "autoId", "autofocus", "minSize"], outputs: ["valueChange", "cleared", "keyupEnter", "keydownEscape", "sdFocus", "sdBlur", "sdKeydown", "sdPaste", "sdCompositionStart", "sdCompositionEnd"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
434
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SdQueryInlineValueChip, isStandalone: true, selector: "sd-query-inline-value-chip", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, operator: { classPropertyName: "operator", publicName: "operator", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, density: { classPropertyName: "density", publicName: "density", isSignal: true, isRequired: false, transformFunction: null }, showOperator: { classPropertyName: "showOperator", publicName: "showOperator", isSignal: true, isRequired: false, transformFunction: null }, autoId: { classPropertyName: "autoId", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, autofocus: { classPropertyName: "autofocus", publicName: "autofocus", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange", remove: "remove" }, viewQueries: [{ propertyName: "firstInput", first: true, predicate: ["firstInput"], descendants: true, isSignal: true }], ngImport: i0, template: "<!--\n Seamless string/number chip \u2014 the pill IS the input. One border, three flush\n segments: label \u00B7 input(s) \u00B7 \u00D7. State styling driven by [data-state]. The value\n field is the shared <sd-inline-text> primitive (chrome=\"seamless\") so width hugs\n content; the chip keeps the envelope + parse/commit logic.\n-->\n@let _state = state();\n@let _autoId = autoId();\n@let _isNoData = isNoData();\n@let _density = density();\n@let _operator = operator();\n\n<!-- why: v\u1ECF chip l\u00E0 m\u1ED9t NH\u00D3M control (nh\u00E3n + \u00F4 nh\u1EADp + n\u00FAt \u00D7), kh\u00F4ng ph\u1EA3i n\u00FAt \u2192 role=\"group\" +\n aria-label m\u00F4 t\u1EA3 \u0111\u00FAng c\u1EA5u tr\u00FAc v\u00E0 c\u0169ng cho bi\u1EBFt v\u00EC sao n\u00F3 KH\u00D4NG nh\u1EADn tabindex (tab \u0111i th\u1EB3ng\n v\u00E0o input b\u00EAn trong). `focusFromShell` \u0111\u00E3 t\u1EF1 lo\u1EA1i click tr\u00EAn input v\u00E0 tr\u00EAn n\u00FAt \u00D7 n\u00EAn g\u1EAFn th\u00EAm\n Enter d\u00F9ng chung handler l\u00E0 an to\u00E0n v\u00E0 ph\u1EA3n chi\u1EBFu \u0111\u00FAng h\u00E0nh vi click. -->\n<span\n class=\"c-seamless\"\n role=\"group\"\n [attr.aria-label]=\"field().label\"\n [attr.data-state]=\"_state\"\n [class.c-seamless--compact]=\"_density === 'compact'\"\n [class.c-seamless--comfortable]=\"_density === 'comfortable'\"\n (click)=\"focusFromShell($event)\"\n (keydown.enter)=\"focusFromShell($event)\">\n\n <!-- Label segment: icon + field name (+ optional operator pill) -->\n <span class=\"c-seamless__label\">\n <sd-icon class=\"c-seamless__icon\" [name]=\"icon()\"></sd-icon>\n <span class=\"c-seamless__field\">{{ field().label }}</span>\n @if (showOperator() && !_isNoData) {\n <sd-operator class=\"c-seamless__op\" [operators]=\"[_operator]\" [model]=\"_operator\" [disabled]=\"true\" />\n } @else if (!_isNoData) {\n <span class=\"c-seamless__sep\">:</span>\n }\n </span>\n\n <!-- Input segment \u2014 seamless borderless primitive; hidden for NULL / NOT_NULL -->\n @if (!_isNoData) {\n <span class=\"c-seamless__input\">\n @if (isBetween()) {\n <sd-inline-text\n #firstInput\n chrome=\"seamless\"\n [clearable]=\"false\"\n [autofocus]=\"autofocus()\"\n [state]=\"_state\"\n [value]=\"draftFrom()\"\n (valueChange)=\"draftFrom.set($event)\"\n [placeholder]=\"phFrom()\"\n [autoId]=\"_autoId ? _autoId + '-from' : undefined\"\n (sdFocus)=\"onFocus()\"\n (sdBlur)=\"commitRange()\"\n (keyupEnter)=\"firstInput.blur()\"\n (keydownEscape)=\"revertAndBlur(firstInput)\" />\n <span class=\"c-seamless__dash\" aria-hidden=\"true\">\u2014</span>\n <sd-inline-text\n #secondInput\n chrome=\"seamless\"\n [clearable]=\"false\"\n [state]=\"_state\"\n [value]=\"draftTo()\"\n (valueChange)=\"draftTo.set($event)\"\n [placeholder]=\"phTo()\"\n [autoId]=\"_autoId ? _autoId + '-to' : undefined\"\n (sdFocus)=\"onFocus()\"\n (sdBlur)=\"commitRange()\"\n (keyupEnter)=\"secondInput.blur()\"\n (keydownEscape)=\"revertAndBlur(secondInput)\" />\n } @else {\n <sd-inline-text\n #firstInput\n chrome=\"seamless\"\n [clearable]=\"false\"\n [autofocus]=\"autofocus()\"\n [state]=\"_state\"\n [value]=\"draft()\"\n (valueChange)=\"draft.set($event)\"\n [placeholder]=\"ph()\"\n [autoId]=\"_autoId ?? undefined\"\n (sdFocus)=\"onFocus()\"\n (sdBlur)=\"commitSingle()\"\n (keyupEnter)=\"firstInput.blur()\"\n (keydownEscape)=\"revertAndBlur(firstInput)\" />\n }\n </span>\n }\n\n <!-- \u00D7 segment -->\n <button type=\"button\" class=\"c-seamless__x\" aria-label=\"Remove filter\" (click)=\"onRemove($event)\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n</span>\n", styles: [":host{display:inline-flex}.c-seamless{display:inline-flex;align-items:stretch;border-radius:999px;overflow:hidden;font-family:inherit;font-size:13px;line-height:1;cursor:text;border:1px solid #d6d8db;background:#fff;color:#5f6368;transition:border-color .12s ease,background-color .12s ease,box-shadow .12s ease,color .12s ease}.c-seamless--compact{height:28px}.c-seamless--comfortable{height:32px}.c-seamless:hover:not([data-state=focus]):not([data-state=error]){border-color:var(--sd-primary)}.c-seamless[data-state=pending]{border-style:dashed;border-color:#d6d8db;background:#fff;color:#5f6368}.c-seamless[data-state=active]{border-color:var(--sd-primary-light);background:var(--sd-primary-light)}.c-seamless[data-state=focus]{border-color:var(--sd-primary);background:#fff;box-shadow:0 0 0 3px var(--sd-primary-light)}.c-seamless[data-state=error]{border-color:#d23535;background:#fff5f5;color:#d23535;box-shadow:0 0 0 3px #d235351f}.c-seamless__label{display:inline-flex;align-items:center;gap:4px;font-weight:500;white-space:nowrap;cursor:text;color:inherit}.c-seamless--compact .c-seamless__label{padding:0 0 0 12px}.c-seamless--comfortable .c-seamless__label{padding:0 0 0 14px}.c-seamless__icon{font-size:14px;width:14px;height:14px;color:inherit}.c-seamless__field{color:inherit}.c-seamless__op{display:inline-flex;align-items:center;color:inherit}.c-seamless__sep{color:#7a7a7a}.c-seamless__input{display:inline-flex;align-items:center;gap:4px}.c-seamless--compact .c-seamless__input{padding:0 8px 0 4px}.c-seamless--comfortable .c-seamless__input{padding:0 10px 0 6px}.c-seamless__dash{color:#7a7a7a}.c-seamless__x{display:inline-flex;align-items:center;justify-content:center;border:0;border-left:1px solid rgba(0,0,0,.04);background:transparent;cursor:pointer;color:inherit}.c-seamless--compact .c-seamless__x{width:26px}.c-seamless--comfortable .c-seamless__x{width:28px}.c-seamless__x sd-icon{font-size:14px;width:14px;height:14px}.c-seamless__x:hover{background:#0000000d}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "component", type: SdOperator, selector: "sd-operator", inputs: ["model", "operators", "disabled", "autoId"], outputs: ["modelChange"] }, { kind: "component", type: SdInlineText, selector: "sd-inline-text", inputs: ["value", "control", "placeholder", "disabled", "clearable", "state", "chrome", "autoId", "autofocus", "minSize"], outputs: ["valueChange", "cleared", "keyupEnter", "keydownEscape", "sdFocus", "sdBlur", "sdKeydown", "sdPaste", "sdCompositionStart", "sdCompositionEnd"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
382
435
  }
383
436
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdQueryInlineValueChip, decorators: [{
384
437
  type: Component,
385
- args: [{ selector: 'sd-query-inline-value-chip', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [SdIcon, SdOperator, SdInlineText], template: "<!--\n Seamless string/number chip \u2014 the pill IS the input. One border, three flush\n segments: label \u00B7 input(s) \u00B7 \u00D7. State styling driven by [data-state]. The value\n field is the shared <sd-inline-text> primitive (chrome=\"seamless\") so width hugs\n content; the chip keeps the envelope + parse/commit logic.\n-->\n@let _state = state();\n@let _autoId = autoId();\n@let _isNoData = isNoData();\n@let _density = density();\n@let _operator = operator();\n\n<span\n class=\"c-seamless\"\n [attr.data-state]=\"_state\"\n [class.c-seamless--compact]=\"_density === 'compact'\"\n [class.c-seamless--comfortable]=\"_density === 'comfortable'\"\n (click)=\"focusFromShell($event)\">\n\n <!-- Label segment: icon + field name (+ optional operator pill) -->\n <span class=\"c-seamless__label\">\n <sd-icon class=\"c-seamless__icon\" [name]=\"icon()\"></sd-icon>\n <span class=\"c-seamless__field\">{{ field().label }}</span>\n @if (showOperator() && !_isNoData) {\n <sd-operator class=\"c-seamless__op\" [operators]=\"[_operator]\" [model]=\"_operator\" [disabled]=\"true\" />\n } @else if (!_isNoData) {\n <span class=\"c-seamless__sep\">:</span>\n }\n </span>\n\n <!-- Input segment \u2014 seamless borderless primitive; hidden for NULL / NOT_NULL -->\n @if (!_isNoData) {\n <span class=\"c-seamless__input\">\n @if (isBetween()) {\n <sd-inline-text\n #firstInput\n chrome=\"seamless\"\n [clearable]=\"false\"\n [autofocus]=\"autofocus()\"\n [state]=\"_state\"\n [value]=\"draftFrom()\"\n (valueChange)=\"draftFrom.set($event)\"\n [placeholder]=\"phFrom()\"\n [autoId]=\"_autoId ? _autoId + '-from' : undefined\"\n (sdFocus)=\"onFocus()\"\n (sdBlur)=\"commitRange()\"\n (keyupEnter)=\"firstInput.blur()\"\n (keydownEscape)=\"revertAndBlur(firstInput)\" />\n <span class=\"c-seamless__dash\" aria-hidden=\"true\">\u2014</span>\n <sd-inline-text\n #secondInput\n chrome=\"seamless\"\n [clearable]=\"false\"\n [state]=\"_state\"\n [value]=\"draftTo()\"\n (valueChange)=\"draftTo.set($event)\"\n [placeholder]=\"phTo()\"\n [autoId]=\"_autoId ? _autoId + '-to' : undefined\"\n (sdFocus)=\"onFocus()\"\n (sdBlur)=\"commitRange()\"\n (keyupEnter)=\"secondInput.blur()\"\n (keydownEscape)=\"revertAndBlur(secondInput)\" />\n } @else {\n <sd-inline-text\n #firstInput\n chrome=\"seamless\"\n [clearable]=\"false\"\n [autofocus]=\"autofocus()\"\n [state]=\"_state\"\n [value]=\"draft()\"\n (valueChange)=\"draft.set($event)\"\n [placeholder]=\"ph()\"\n [autoId]=\"_autoId ?? undefined\"\n (sdFocus)=\"onFocus()\"\n (sdBlur)=\"commitSingle()\"\n (keyupEnter)=\"firstInput.blur()\"\n (keydownEscape)=\"revertAndBlur(firstInput)\" />\n }\n </span>\n }\n\n <!-- \u00D7 segment -->\n <button type=\"button\" class=\"c-seamless__x\" aria-label=\"Remove filter\" (click)=\"onRemove($event)\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n</span>\n", styles: [":host{display:inline-flex}.c-seamless{display:inline-flex;align-items:stretch;border-radius:999px;overflow:hidden;font-family:inherit;font-size:13px;line-height:1;cursor:text;border:1px solid #d6d8db;background:#fff;color:#5f6368;transition:border-color .12s ease,background-color .12s ease,box-shadow .12s ease,color .12s ease}.c-seamless--compact{height:28px}.c-seamless--comfortable{height:32px}.c-seamless:hover:not([data-state=focus]):not([data-state=error]){border-color:var(--sd-primary)}.c-seamless[data-state=pending]{border-style:dashed;border-color:#d6d8db;background:#fff;color:#5f6368}.c-seamless[data-state=active]{border-color:var(--sd-primary-light);background:var(--sd-primary-light)}.c-seamless[data-state=focus]{border-color:var(--sd-primary);background:#fff;box-shadow:0 0 0 3px var(--sd-primary-light)}.c-seamless[data-state=error]{border-color:#d23535;background:#fff5f5;color:#d23535;box-shadow:0 0 0 3px #d235351f}.c-seamless__label{display:inline-flex;align-items:center;gap:4px;font-weight:500;white-space:nowrap;cursor:text;color:inherit}.c-seamless--compact .c-seamless__label{padding:0 0 0 12px}.c-seamless--comfortable .c-seamless__label{padding:0 0 0 14px}.c-seamless__icon{font-size:14px;width:14px;height:14px;color:inherit}.c-seamless__field{color:inherit}.c-seamless__op{display:inline-flex;align-items:center;color:inherit}.c-seamless__sep{color:#7a7a7a}.c-seamless__input{display:inline-flex;align-items:center;gap:4px}.c-seamless--compact .c-seamless__input{padding:0 8px 0 4px}.c-seamless--comfortable .c-seamless__input{padding:0 10px 0 6px}.c-seamless__dash{color:#7a7a7a}.c-seamless__x{display:inline-flex;align-items:center;justify-content:center;border:0;border-left:1px solid rgba(0,0,0,.04);background:transparent;cursor:pointer;color:inherit}.c-seamless--compact .c-seamless__x{width:26px}.c-seamless--comfortable .c-seamless__x{width:28px}.c-seamless__x sd-icon{font-size:14px;width:14px;height:14px}.c-seamless__x:hover{background:#0000000d}\n"] }]
438
+ args: [{ selector: 'sd-query-inline-value-chip', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [SdIcon, SdOperator, SdInlineText], template: "<!--\n Seamless string/number chip \u2014 the pill IS the input. One border, three flush\n segments: label \u00B7 input(s) \u00B7 \u00D7. State styling driven by [data-state]. The value\n field is the shared <sd-inline-text> primitive (chrome=\"seamless\") so width hugs\n content; the chip keeps the envelope + parse/commit logic.\n-->\n@let _state = state();\n@let _autoId = autoId();\n@let _isNoData = isNoData();\n@let _density = density();\n@let _operator = operator();\n\n<!-- why: v\u1ECF chip l\u00E0 m\u1ED9t NH\u00D3M control (nh\u00E3n + \u00F4 nh\u1EADp + n\u00FAt \u00D7), kh\u00F4ng ph\u1EA3i n\u00FAt \u2192 role=\"group\" +\n aria-label m\u00F4 t\u1EA3 \u0111\u00FAng c\u1EA5u tr\u00FAc v\u00E0 c\u0169ng cho bi\u1EBFt v\u00EC sao n\u00F3 KH\u00D4NG nh\u1EADn tabindex (tab \u0111i th\u1EB3ng\n v\u00E0o input b\u00EAn trong). `focusFromShell` \u0111\u00E3 t\u1EF1 lo\u1EA1i click tr\u00EAn input v\u00E0 tr\u00EAn n\u00FAt \u00D7 n\u00EAn g\u1EAFn th\u00EAm\n Enter d\u00F9ng chung handler l\u00E0 an to\u00E0n v\u00E0 ph\u1EA3n chi\u1EBFu \u0111\u00FAng h\u00E0nh vi click. -->\n<span\n class=\"c-seamless\"\n role=\"group\"\n [attr.aria-label]=\"field().label\"\n [attr.data-state]=\"_state\"\n [class.c-seamless--compact]=\"_density === 'compact'\"\n [class.c-seamless--comfortable]=\"_density === 'comfortable'\"\n (click)=\"focusFromShell($event)\"\n (keydown.enter)=\"focusFromShell($event)\">\n\n <!-- Label segment: icon + field name (+ optional operator pill) -->\n <span class=\"c-seamless__label\">\n <sd-icon class=\"c-seamless__icon\" [name]=\"icon()\"></sd-icon>\n <span class=\"c-seamless__field\">{{ field().label }}</span>\n @if (showOperator() && !_isNoData) {\n <sd-operator class=\"c-seamless__op\" [operators]=\"[_operator]\" [model]=\"_operator\" [disabled]=\"true\" />\n } @else if (!_isNoData) {\n <span class=\"c-seamless__sep\">:</span>\n }\n </span>\n\n <!-- Input segment \u2014 seamless borderless primitive; hidden for NULL / NOT_NULL -->\n @if (!_isNoData) {\n <span class=\"c-seamless__input\">\n @if (isBetween()) {\n <sd-inline-text\n #firstInput\n chrome=\"seamless\"\n [clearable]=\"false\"\n [autofocus]=\"autofocus()\"\n [state]=\"_state\"\n [value]=\"draftFrom()\"\n (valueChange)=\"draftFrom.set($event)\"\n [placeholder]=\"phFrom()\"\n [autoId]=\"_autoId ? _autoId + '-from' : undefined\"\n (sdFocus)=\"onFocus()\"\n (sdBlur)=\"commitRange()\"\n (keyupEnter)=\"firstInput.blur()\"\n (keydownEscape)=\"revertAndBlur(firstInput)\" />\n <span class=\"c-seamless__dash\" aria-hidden=\"true\">\u2014</span>\n <sd-inline-text\n #secondInput\n chrome=\"seamless\"\n [clearable]=\"false\"\n [state]=\"_state\"\n [value]=\"draftTo()\"\n (valueChange)=\"draftTo.set($event)\"\n [placeholder]=\"phTo()\"\n [autoId]=\"_autoId ? _autoId + '-to' : undefined\"\n (sdFocus)=\"onFocus()\"\n (sdBlur)=\"commitRange()\"\n (keyupEnter)=\"secondInput.blur()\"\n (keydownEscape)=\"revertAndBlur(secondInput)\" />\n } @else {\n <sd-inline-text\n #firstInput\n chrome=\"seamless\"\n [clearable]=\"false\"\n [autofocus]=\"autofocus()\"\n [state]=\"_state\"\n [value]=\"draft()\"\n (valueChange)=\"draft.set($event)\"\n [placeholder]=\"ph()\"\n [autoId]=\"_autoId ?? undefined\"\n (sdFocus)=\"onFocus()\"\n (sdBlur)=\"commitSingle()\"\n (keyupEnter)=\"firstInput.blur()\"\n (keydownEscape)=\"revertAndBlur(firstInput)\" />\n }\n </span>\n }\n\n <!-- \u00D7 segment -->\n <button type=\"button\" class=\"c-seamless__x\" aria-label=\"Remove filter\" (click)=\"onRemove($event)\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n</span>\n", styles: [":host{display:inline-flex}.c-seamless{display:inline-flex;align-items:stretch;border-radius:999px;overflow:hidden;font-family:inherit;font-size:13px;line-height:1;cursor:text;border:1px solid #d6d8db;background:#fff;color:#5f6368;transition:border-color .12s ease,background-color .12s ease,box-shadow .12s ease,color .12s ease}.c-seamless--compact{height:28px}.c-seamless--comfortable{height:32px}.c-seamless:hover:not([data-state=focus]):not([data-state=error]){border-color:var(--sd-primary)}.c-seamless[data-state=pending]{border-style:dashed;border-color:#d6d8db;background:#fff;color:#5f6368}.c-seamless[data-state=active]{border-color:var(--sd-primary-light);background:var(--sd-primary-light)}.c-seamless[data-state=focus]{border-color:var(--sd-primary);background:#fff;box-shadow:0 0 0 3px var(--sd-primary-light)}.c-seamless[data-state=error]{border-color:#d23535;background:#fff5f5;color:#d23535;box-shadow:0 0 0 3px #d235351f}.c-seamless__label{display:inline-flex;align-items:center;gap:4px;font-weight:500;white-space:nowrap;cursor:text;color:inherit}.c-seamless--compact .c-seamless__label{padding:0 0 0 12px}.c-seamless--comfortable .c-seamless__label{padding:0 0 0 14px}.c-seamless__icon{font-size:14px;width:14px;height:14px;color:inherit}.c-seamless__field{color:inherit}.c-seamless__op{display:inline-flex;align-items:center;color:inherit}.c-seamless__sep{color:#7a7a7a}.c-seamless__input{display:inline-flex;align-items:center;gap:4px}.c-seamless--compact .c-seamless__input{padding:0 8px 0 4px}.c-seamless--comfortable .c-seamless__input{padding:0 10px 0 6px}.c-seamless__dash{color:#7a7a7a}.c-seamless__x{display:inline-flex;align-items:center;justify-content:center;border:0;border-left:1px solid rgba(0,0,0,.04);background:transparent;cursor:pointer;color:inherit}.c-seamless--compact .c-seamless__x{width:26px}.c-seamless--comfortable .c-seamless__x{width:28px}.c-seamless__x sd-icon{font-size:14px;width:14px;height:14px}.c-seamless__x:hover{background:#0000000d}\n"] }]
386
439
  }], ctorParameters: () => [], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], operator: [{ type: i0.Input, args: [{ isSignal: true, alias: "operator", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], density: [{ type: i0.Input, args: [{ isSignal: true, alias: "density", required: false }] }], showOperator: [{ type: i0.Input, args: [{ isSignal: true, alias: "showOperator", required: false }] }], autoId: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoId", required: false }] }], autofocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "autofocus", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], remove: [{ type: i0.Output, args: ["remove"] }], firstInput: [{ type: i0.ViewChild, args: ['firstInput', { isSignal: true }] }] } });
387
440
 
388
441
  /**
@@ -400,6 +453,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
400
453
  * operator step, disabled badge in value step) + the per-type picker + × cancel.
401
454
  */
402
455
  class SdQueryBuildChip {
456
+ #i18n = inject(I18nService);
457
+ // why: nhãn mặc định của field boolean phải trùng với nhãn parent dựng cho chip đã hoàn thành
458
+ // (`SdQueryBar.chipValueText`), nếu không cùng một field sẽ hiện hai ngôn ngữ giữa lúc đang tạo
459
+ // chip và lúc chip xong.
460
+ booleanTrueLabel = computed(() => this.#i18n.t('core.component.query-bar.boolean.true'), ...(ngDevMode ? [{ debugName: "booleanTrueLabel" }] : /* istanbul ignore next */ []));
461
+ booleanFalseLabel = computed(() => this.#i18n.t('core.component.query-bar.boolean.false'), ...(ngDevMode ? [{ debugName: "booleanFalseLabel" }] : /* istanbul ignore next */ []));
403
462
  /** Current build state — drives every visual branch. */
404
463
  building = input.required(...(ngDevMode ? [{ debugName: "building" }] : /* istanbul ignore next */ []));
405
464
  /** Density preset — forwarded to the seamless chip + sizes the .c-token row. */
@@ -464,11 +523,11 @@ class SdQueryBuildChip {
464
523
  return t === 'string' || t === 'number';
465
524
  }, ...(ngDevMode ? [{ debugName: "isSeamless" }] : /* istanbul ignore next */ []));
466
525
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdQueryBuildChip, deps: [], target: i0.ɵɵFactoryTarget.Component });
467
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SdQueryBuildChip, isStandalone: true, selector: "sd-query-build-chip", inputs: { building: { classPropertyName: "building", publicName: "building", isSignal: true, isRequired: true, transformFunction: null }, density: { classPropertyName: "density", publicName: "density", isSignal: true, isRequired: false, transformFunction: null }, allowedOperators: { classPropertyName: "allowedOperators", publicName: "allowedOperators", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, showOperator: { classPropertyName: "showOperator", publicName: "showOperator", isSignal: true, isRequired: false, transformFunction: null }, autoId: { classPropertyName: "autoId", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pickOperator: "pickOperator", commitValue: "commitValue", cancel: "cancel", seamlessCommit: "seamlessCommit", draftChange: "draftChange", draftCommit: "draftCommit" }, viewQueries: [{ propertyName: "opMenu", first: true, predicate: ["buildOperator"], descendants: true, isSignal: true }, { propertyName: "picker", first: true, predicate: ["bPicker"], descendants: true, isSignal: true }], ngImport: i0, template: "<!--\n In-progress chip (inline-mode token build flow).\n Seamless string/number branch keeps its own pill; everything else renders inside\n the dashed .c-token.c-token-building shell.\n-->\n@let _building = building();\n@let _autoId = autoId();\n\n@if (isSeamless()) {\n <sd-query-inline-value-chip\n [field]=\"_building.field\"\n [operator]=\"$any(_building.operator)\"\n [value]=\"_building.value ?? null\"\n [density]=\"density()\"\n [showOperator]=\"showOperator()\"\n [autoId]=\"_autoId\"\n [autofocus]=\"true\"\n (valueChange)=\"seamlessCommit.emit($event)\"\n (remove)=\"cancel.emit()\" />\n} @else {\n <div class=\"c-token c-token-building\">\n <span class=\"c-token-field\">{{ _building.field.label }}</span>\n\n @if (_building.step === 'operator') {\n <sd-operator\n #buildOperator\n class=\"c-token-op\"\n [operators]=\"allowedOperators()\"\n [model]=\"_building.operator\"\n (modelChange)=\"pickOperator.emit($any($event))\" />\n } @else {\n <!-- why: badge readonly cho operator \u0111\u00E3 ch\u1ECDn \u2014 v\u1EABn render <sd-operator>\n \u0111\u1EC3 gi\u1EEF th\u1ECB gi\u00E1c gi\u1ED1ng chip ho\u00E0n th\u00E0nh (icon + tooltip). -->\n <sd-operator\n class=\"c-token-op\"\n [operators]=\"[$any(_building.operator)]\"\n [model]=\"$any(_building.operator)\"\n [disabled]=\"true\" />\n\n @if (_building.field.type === 'values' || _building.field.type === 'lazy-values') {\n @let _bopt = $any(_building.field).option;\n <span class=\"c-token-value c-token-value-edit\">\n <sd-select #bPicker size=\"sm\" [viewed]=\"'inline'\" [clearable]=\"false\" minWidthPanel=\"300px\"\n [autoId]=\"_autoId\"\n [items]=\"_building.field.type === 'lazy-values' ? $any(_bopt).search : $any(_bopt.items)\"\n [valueField]=\"_bopt.valueField\"\n [displayField]=\"_bopt.displayField\"\n [multiple]=\"multiple()\"\n [model]=\"$any(_building.value)\"\n (sdChange)=\"commitValue.emit($event)\">\n <!-- why: build chip multi-select c\u0169ng c\u1EA7n \"head +N\" \u0111\u1EC3 g\u1ECDn (gi\u1ED1ng completed chip). -->\n <ng-template #sdValue let-selectedItems=\"selectedItems\">\n @if ((selectedItems?.length ?? 0) > 1) {\n <span class=\"c-token-multi-head\">{{ $any(selectedItems)[0][_bopt.displayField] }}</span> +{{ selectedItems.length - 1 }}\n } @else if ((selectedItems?.length ?? 0) === 1) {\n <span class=\"c-token-multi-head\">{{ $any(selectedItems)[0][_bopt.displayField] }}</span>\n }\n </ng-template>\n </sd-select>\n </span>\n } @else if (_building.field.type === 'date') {\n <span class=\"c-token-value c-token-value-edit\">\n @if (_building.operator === 'BETWEEN') {\n <!-- why: 1 sd-date-range thay 2 sd-date \u2014 g\u1ECDn h\u01A1n, h\u00E0nh vi th\u1ED1ng nh\u1EA5t v\u1EDBi completed chip. -->\n <sd-date-range #bPicker size=\"sm\" [viewed]=\"'inline'\" [clearable]=\"false\"\n [autoId]=\"_autoId\"\n [model]=\"$any(_building.value)\"\n (sdChange)=\"commitValue.emit($event)\"></sd-date-range>\n } @else {\n <sd-date #bPicker size=\"sm\" [viewed]=\"'inline'\" [clearable]=\"false\"\n [autoId]=\"_autoId\"\n [model]=\"$any(_building.value)\"\n (sdChange)=\"commitValue.emit($event)\"></sd-date>\n }\n </span>\n } @else if (_building.field.type === 'datetime') {\n <span class=\"c-token-value c-token-value-edit\">\n @if (_building.operator === 'BETWEEN') {\n <!-- why: datetime BETWEEN downgrade v\u1EC1 date-range (m\u1EA5t time precision; \u0111\u00E3 th\u1ED1ng nh\u1EA5t). -->\n <sd-date-range #bPicker size=\"sm\" [viewed]=\"'inline'\" [clearable]=\"false\"\n [autoId]=\"_autoId\"\n [model]=\"$any(_building.value)\"\n (sdChange)=\"commitValue.emit($event)\"></sd-date-range>\n } @else {\n <sd-datetime #bPicker size=\"sm\" [viewed]=\"'inline'\" [clearable]=\"false\"\n [autoId]=\"_autoId\"\n [model]=\"$any(_building.value)\"\n (sdChange)=\"commitValue.emit($event)\"></sd-datetime>\n }\n </span>\n } @else if (_building.field.type === 'boolean') {\n <!-- why: native button thay sd-button (tr\u00E1nh mat-button baseline 36px g\u00E2y tr\u00E0n).\n Pill nh\u1ECF v\u1EBD b\u1EB1ng CSS scoped trong chip \u2014 gi\u1EEF semantic colour qua class\n .c-bool-true / .c-bool-false. -->\n <span class=\"c-token-value c-token-value-edit c-bool-toggle\">\n <button\n type=\"button\"\n class=\"c-bool-btn c-bool-true\"\n [class.c-bool-active]=\"_building.value === true\"\n [attr.data-autoId]=\"_autoId + '-true'\"\n (click)=\"commitValue.emit(true)\">\n {{ $any(_building.field).trueLabel || 'C\u00F3' }}\n </button>\n <button\n type=\"button\"\n class=\"c-bool-btn c-bool-false\"\n [class.c-bool-active]=\"_building.value === false\"\n [attr.data-autoId]=\"_autoId + '-false'\"\n (click)=\"commitValue.emit(false)\">\n {{ $any(_building.field).falseLabel || 'Kh\u00F4ng' }}\n </button>\n </span>\n } @else {\n <!-- Fallback (e.g. unforeseen type) \u2014 generic sd-input wired via draft outputs. -->\n <span class=\"c-token-value c-token-value-edit\">\n <sd-input size=\"sm\"\n [autoId]=\"_autoId\"\n [model]=\"$any(_building.value)\"\n (sdChange)=\"draftChange.emit($event)\"\n (keyupEnter)=\"draftCommit.emit()\"></sd-input>\n </span>\n }\n }\n\n <button type=\"button\" class=\"c-token-remove\" aria-label=\"Cancel\" (click)=\"cancel.emit()\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n </div>\n}\n", styles: [":host{display:contents}.c-token{display:inline-flex;align-items:center;gap:4px;padding:0 0 0 12px;border:1px solid var(--sd-primary-light);border-radius:999px;background:var(--sd-primary-light);font-size:13px;color:#5f6368;white-space:nowrap;overflow:hidden}:host-context(.c-density-compact) .c-token{height:28px}:host-context(.c-density-comfortable) .c-token{height:32px}.c-token-field{font-weight:500;line-height:1}.c-token-op{display:inline-flex;align-items:center;color:inherit}.c-token-op ::ng-deep .c-op-trigger{padding:0;color:inherit}.c-token-op ::ng-deep .c-op-icon svg{width:14px;height:14px;display:block}.c-token-value{border:none;background:transparent;padding:0;font:inherit;font-weight:500;color:var(--sd-primary)}.c-token-value-edit{display:inline-flex;align-items:center;gap:4px;max-width:240px}.c-token-value-edit sd-input,.c-token-value-edit sd-input-number{display:inline-flex;max-width:140px}.c-token-value-edit sd-date-range{display:inline-flex;max-width:220px;min-width:0;flex:0 1 auto}.c-token-value-edit sd-select,.c-token-value-edit sd-date,.c-token-value-edit sd-datetime{display:inline-flex;align-items:center;line-height:1}.c-token ::ng-deep .mat-mdc-form-field{line-height:1}.c-token ::ng-deep .mat-mdc-text-field-wrapper,.c-token ::ng-deep .mat-mdc-form-field-flex{padding:0;min-height:0;background:transparent}.c-token ::ng-deep .mat-mdc-form-field-infix{padding:0;min-height:0;width:auto;border:0;display:inline-flex;align-items:center}.c-token ::ng-deep .mat-mdc-select-value,.c-token ::ng-deep .mat-mdc-input-element,.c-token ::ng-deep input{line-height:1;font-size:13px;padding:0;height:auto;color:inherit}.c-token ::ng-deep .mat-mdc-select-value{display:inline-flex;align-items:center;min-width:0;max-width:200px;overflow:hidden;text-overflow:ellipsis;color:var(--sd-primary);font-weight:500}.c-token ::ng-deep .sd-suffix-icon{display:none}.c-token-multi-head{display:inline-block;max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom}.c-token-building{border-style:dashed}.c-bool-toggle{display:inline-flex;align-items:center;gap:4px}.c-bool-btn{display:inline-flex;align-items:center;justify-content:center;height:18px;min-width:0;padding:0 8px;border:1px solid currentColor;border-radius:999px;background:transparent;font:inherit;font-size:11px;font-weight:500;line-height:1;cursor:pointer;white-space:nowrap;-webkit-user-select:none;user-select:none;transition:background .12s ease,color .12s ease}:host-context(.c-density-comfortable) .c-bool-btn{height:20px;padding:0 10px;font-size:12px}.c-bool-true{color:var(--sd-success)}.c-bool-false{color:var(--sd-warning)}.c-bool-active.c-bool-true{background:var(--sd-success);color:#fff;border-color:var(--sd-success)}.c-bool-active.c-bool-false{background:var(--sd-warning);color:#fff;border-color:var(--sd-warning)}.c-bool-btn:hover:not(.c-bool-active).c-bool-true{background:var(--sd-success-light)}.c-bool-btn:hover:not(.c-bool-active).c-bool-false{background:var(--sd-warning-light)}.c-token-remove{align-self:stretch;display:inline-flex;align-items:center;justify-content:center;border:none;border-left:1px solid rgba(0,0,0,.04);border-radius:0;background:transparent;cursor:pointer;color:inherit}:host-context(.c-density-compact) .c-token-remove{width:26px}:host-context(.c-density-comfortable) .c-token-remove{width:28px}.c-token-remove sd-icon{font-size:14px;width:14px;height:14px}.c-token-remove:hover{background:#0000000d}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "ngmodule", type: CommonModule }, { kind: "component", type: SdOperator, selector: "sd-operator", inputs: ["model", "operators", "disabled", "autoId"], outputs: ["modelChange"] }, { kind: "component", type: SdDate, selector: "sd-date", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "required", "disabled", "viewed", "clearable", "inlineError", "hyperlink", "appearance", "floatLabel", "min", "minDate", "max", "maxDate", "model"], outputs: ["modelChange", "sdChange", "sdFocus"] }, { kind: "component", type: SdDateRange, selector: "sd-date-range", inputs: ["autoId", "name", "size", "form", "label", "helperText", "hideInlineError", "required", "disabled", "viewed", "clearable", "appearance", "floatLabel", "min", "max", "model"], outputs: ["modelChange", "sdChange"] }, { kind: "component", type: SdDatetime, selector: "sd-datetime", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "required", "disabled", "viewed", "showSeconds", "clearable", "inlineError", "hyperlink", "appearance", "floatLabel", "min", "minDate", "max", "maxDate", "model"], outputs: ["modelChange", "sdChange", "sdFocus"] }, { kind: "component", type: SdInput, selector: "sd-input", inputs: ["autoId", "name", "appearance", "floatLabel", "size", "form", "label", "helperText", "placeholder", "type", "mask", "hideInlineError", "blurOnEnter", "clearable", "required", "readonly", "disabled", "viewed", "minlength", "maxlength", "pattern", "patternErrorMessage", "validator", "inlineError", "hyperlink", "model"], outputs: ["modelChange", "sdChange", "sdFocus", "sdBlur", "keyupEnter", "cleared", "sdFocusForceBlur"] }, { kind: "component", type: SdSelect, selector: "sd-select", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "valueField", "displayField", "disabledField", "cacheChecksum", "limit", "hyperlink", "minWidthPanel", "hideInlineError", "required", "disabled", "viewed", "multiple", "clearable", "validator", "inlineError", "appearance", "floatLabel", "items", "model"], outputs: ["modelChange", "sdChange", "sdSelection"] }, { kind: "component", type: SdQueryInlineValueChip, selector: "sd-query-inline-value-chip", inputs: ["field", "operator", "value", "density", "showOperator", "autoId", "autofocus"], outputs: ["valueChange", "remove"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
526
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SdQueryBuildChip, isStandalone: true, selector: "sd-query-build-chip", inputs: { building: { classPropertyName: "building", publicName: "building", isSignal: true, isRequired: true, transformFunction: null }, density: { classPropertyName: "density", publicName: "density", isSignal: true, isRequired: false, transformFunction: null }, allowedOperators: { classPropertyName: "allowedOperators", publicName: "allowedOperators", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, showOperator: { classPropertyName: "showOperator", publicName: "showOperator", isSignal: true, isRequired: false, transformFunction: null }, autoId: { classPropertyName: "autoId", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pickOperator: "pickOperator", commitValue: "commitValue", cancel: "cancel", seamlessCommit: "seamlessCommit", draftChange: "draftChange", draftCommit: "draftCommit" }, viewQueries: [{ propertyName: "opMenu", first: true, predicate: ["buildOperator"], descendants: true, isSignal: true }, { propertyName: "picker", first: true, predicate: ["bPicker"], descendants: true, isSignal: true }], ngImport: i0, template: "<!--\n In-progress chip (inline-mode token build flow).\n Seamless string/number branch keeps its own pill; everything else renders inside\n the dashed .c-token.c-token-building shell.\n-->\n@let _building = building();\n@let _autoId = autoId();\n\n@if (isSeamless()) {\n <sd-query-inline-value-chip\n [field]=\"_building.field\"\n [operator]=\"$any(_building.operator)\"\n [value]=\"_building.value ?? null\"\n [density]=\"density()\"\n [showOperator]=\"showOperator()\"\n [autoId]=\"_autoId\"\n [autofocus]=\"true\"\n (valueChange)=\"seamlessCommit.emit($event)\"\n (remove)=\"cancel.emit()\" />\n} @else {\n <div class=\"c-token c-token-building\">\n <span class=\"c-token-field\">{{ _building.field.label }}</span>\n\n @if (_building.step === 'operator') {\n <sd-operator\n #buildOperator\n class=\"c-token-op\"\n [operators]=\"allowedOperators()\"\n [model]=\"_building.operator\"\n (modelChange)=\"pickOperator.emit($any($event))\" />\n } @else {\n <!-- why: badge readonly cho operator \u0111\u00E3 ch\u1ECDn \u2014 v\u1EABn render <sd-operator>\n \u0111\u1EC3 gi\u1EEF th\u1ECB gi\u00E1c gi\u1ED1ng chip ho\u00E0n th\u00E0nh (icon + tooltip). -->\n <sd-operator\n class=\"c-token-op\"\n [operators]=\"[$any(_building.operator)]\"\n [model]=\"$any(_building.operator)\"\n [disabled]=\"true\" />\n\n @if (_building.field.type === 'values' || _building.field.type === 'lazy-values') {\n @let _bopt = $any(_building.field).option;\n <span class=\"c-token-value c-token-value-edit\">\n <sd-select #bPicker size=\"sm\" [viewed]=\"'inline'\" [clearable]=\"false\" minWidthPanel=\"300px\"\n [autoId]=\"_autoId\"\n [items]=\"_building.field.type === 'lazy-values' ? $any(_bopt).search : $any(_bopt.items)\"\n [valueField]=\"_bopt.valueField\"\n [displayField]=\"_bopt.displayField\"\n [multiple]=\"multiple()\"\n [model]=\"$any(_building.value)\"\n (sdChange)=\"commitValue.emit($event)\">\n <!-- why: build chip multi-select c\u0169ng c\u1EA7n \"head +N\" \u0111\u1EC3 g\u1ECDn (gi\u1ED1ng completed chip). -->\n <ng-template #sdValue let-selectedItems=\"selectedItems\">\n @if ((selectedItems?.length ?? 0) > 1) {\n <span class=\"c-token-multi-head\">{{ $any(selectedItems)[0][_bopt.displayField] }}</span> +{{ selectedItems.length - 1 }}\n } @else if ((selectedItems?.length ?? 0) === 1) {\n <span class=\"c-token-multi-head\">{{ $any(selectedItems)[0][_bopt.displayField] }}</span>\n }\n </ng-template>\n </sd-select>\n </span>\n } @else if (_building.field.type === 'date') {\n <span class=\"c-token-value c-token-value-edit\">\n @if (_building.operator === 'BETWEEN') {\n <!-- why: 1 sd-date-range thay 2 sd-date \u2014 g\u1ECDn h\u01A1n, h\u00E0nh vi th\u1ED1ng nh\u1EA5t v\u1EDBi completed chip. -->\n <sd-date-range #bPicker size=\"sm\" [viewed]=\"'inline'\" [clearable]=\"false\"\n [autoId]=\"_autoId\"\n [model]=\"$any(_building.value)\"\n (sdChange)=\"commitValue.emit($event)\"></sd-date-range>\n } @else {\n <sd-date #bPicker size=\"sm\" [viewed]=\"'inline'\" [clearable]=\"false\"\n [autoId]=\"_autoId\"\n [model]=\"$any(_building.value)\"\n (sdChange)=\"commitValue.emit($event)\"></sd-date>\n }\n </span>\n } @else if (_building.field.type === 'datetime') {\n <span class=\"c-token-value c-token-value-edit\">\n @if (_building.operator === 'BETWEEN') {\n <!-- why: datetime BETWEEN downgrade v\u1EC1 date-range (m\u1EA5t time precision; \u0111\u00E3 th\u1ED1ng nh\u1EA5t). -->\n <sd-date-range #bPicker size=\"sm\" [viewed]=\"'inline'\" [clearable]=\"false\"\n [autoId]=\"_autoId\"\n [model]=\"$any(_building.value)\"\n (sdChange)=\"commitValue.emit($event)\"></sd-date-range>\n } @else {\n <sd-datetime #bPicker size=\"sm\" [viewed]=\"'inline'\" [clearable]=\"false\"\n [autoId]=\"_autoId\"\n [model]=\"$any(_building.value)\"\n (sdChange)=\"commitValue.emit($event)\"></sd-datetime>\n }\n </span>\n } @else if (_building.field.type === 'boolean') {\n <!-- why: native button thay sd-button (tr\u00E1nh mat-button baseline 36px g\u00E2y tr\u00E0n).\n Pill nh\u1ECF v\u1EBD b\u1EB1ng CSS scoped trong chip \u2014 gi\u1EEF semantic colour qua class\n .c-bool-true / .c-bool-false. -->\n <span class=\"c-token-value c-token-value-edit c-bool-toggle\">\n <button\n type=\"button\"\n class=\"c-bool-btn c-bool-true\"\n [class.c-bool-active]=\"_building.value === true\"\n [attr.data-autoId]=\"_autoId + '-true'\"\n (click)=\"commitValue.emit(true)\">\n {{ $any(_building.field).trueLabel || booleanTrueLabel() }}\n </button>\n <button\n type=\"button\"\n class=\"c-bool-btn c-bool-false\"\n [class.c-bool-active]=\"_building.value === false\"\n [attr.data-autoId]=\"_autoId + '-false'\"\n (click)=\"commitValue.emit(false)\">\n {{ $any(_building.field).falseLabel || booleanFalseLabel() }}\n </button>\n </span>\n } @else {\n <!-- Fallback (e.g. unforeseen type) \u2014 generic sd-input wired via draft outputs. -->\n <span class=\"c-token-value c-token-value-edit\">\n <sd-input size=\"sm\"\n [autoId]=\"_autoId\"\n [model]=\"$any(_building.value)\"\n (sdChange)=\"draftChange.emit($event)\"\n (keyupEnter)=\"draftCommit.emit()\"></sd-input>\n </span>\n }\n }\n\n <button type=\"button\" class=\"c-token-remove\" aria-label=\"Cancel\" (click)=\"cancel.emit()\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n </div>\n}\n", styles: [":host{display:contents}.c-token{display:inline-flex;align-items:center;gap:4px;padding:0 0 0 12px;border:1px solid var(--sd-primary-light);border-radius:999px;background:var(--sd-primary-light);font-size:13px;color:#5f6368;white-space:nowrap;overflow:hidden}:host-context(.c-density-compact) .c-token{height:28px}:host-context(.c-density-comfortable) .c-token{height:32px}.c-token-field{font-weight:500;line-height:1}.c-token-op{display:inline-flex;align-items:center;color:inherit}.c-token-op ::ng-deep .c-op-trigger{padding:0;color:inherit}.c-token-op ::ng-deep .c-op-icon svg{width:14px;height:14px;display:block}.c-token-value{border:none;background:transparent;padding:0;font:inherit;font-weight:500;color:var(--sd-primary)}.c-token-value-edit{display:inline-flex;align-items:center;gap:4px;max-width:240px}.c-token-value-edit sd-input,.c-token-value-edit sd-input-number{display:inline-flex;max-width:140px}.c-token-value-edit sd-date-range{display:inline-flex;max-width:220px;min-width:0;flex:0 1 auto}.c-token-value-edit sd-select,.c-token-value-edit sd-date,.c-token-value-edit sd-datetime{display:inline-flex;align-items:center;line-height:1}.c-token ::ng-deep .mat-mdc-form-field{line-height:1}.c-token ::ng-deep .mat-mdc-text-field-wrapper,.c-token ::ng-deep .mat-mdc-form-field-flex{padding:0;min-height:0;background:transparent}.c-token ::ng-deep .mat-mdc-form-field-infix{padding:0;min-height:0;width:auto;border:0;display:inline-flex;align-items:center}.c-token ::ng-deep .mat-mdc-select-value,.c-token ::ng-deep .mat-mdc-input-element,.c-token ::ng-deep input{line-height:1;font-size:13px;padding:0;height:auto;color:inherit}.c-token ::ng-deep .mat-mdc-select-value{display:inline-flex;align-items:center;min-width:0;max-width:200px;overflow:hidden;text-overflow:ellipsis;color:var(--sd-primary);font-weight:500}.c-token ::ng-deep .sd-suffix-icon{display:none}.c-token-multi-head{display:inline-block;max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom}.c-token-building{border-style:dashed}.c-bool-toggle{display:inline-flex;align-items:center;gap:4px}.c-bool-btn{display:inline-flex;align-items:center;justify-content:center;height:18px;min-width:0;padding:0 8px;border:1px solid currentColor;border-radius:999px;background:transparent;font:inherit;font-size:11px;font-weight:500;line-height:1;cursor:pointer;white-space:nowrap;-webkit-user-select:none;user-select:none;transition:background .12s ease,color .12s ease}:host-context(.c-density-comfortable) .c-bool-btn{height:20px;padding:0 10px;font-size:12px}.c-bool-true{color:var(--sd-success)}.c-bool-false{color:var(--sd-warning)}.c-bool-active.c-bool-true{background:var(--sd-success);color:#fff;border-color:var(--sd-success)}.c-bool-active.c-bool-false{background:var(--sd-warning);color:#fff;border-color:var(--sd-warning)}.c-bool-btn:hover:not(.c-bool-active).c-bool-true{background:var(--sd-success-light)}.c-bool-btn:hover:not(.c-bool-active).c-bool-false{background:var(--sd-warning-light)}.c-token-remove{align-self:stretch;display:inline-flex;align-items:center;justify-content:center;border:none;border-left:1px solid rgba(0,0,0,.04);border-radius:0;background:transparent;cursor:pointer;color:inherit}:host-context(.c-density-compact) .c-token-remove{width:26px}:host-context(.c-density-comfortable) .c-token-remove{width:28px}.c-token-remove sd-icon{font-size:14px;width:14px;height:14px}.c-token-remove:hover{background:#0000000d}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "ngmodule", type: CommonModule }, { kind: "component", type: SdOperator, selector: "sd-operator", inputs: ["model", "operators", "disabled", "autoId"], outputs: ["modelChange"] }, { kind: "component", type: SdDate, selector: "sd-date", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "required", "disabled", "viewed", "clearable", "inlineError", "hyperlink", "appearance", "floatLabel", "min", "minDate", "max", "maxDate", "transform", "model"], outputs: ["modelChange", "sdChange", "sdFocus"] }, { kind: "component", type: SdDateRange, selector: "sd-date-range", inputs: ["autoId", "name", "size", "form", "label", "helperText", "hideInlineError", "required", "disabled", "viewed", "clearable", "appearance", "floatLabel", "min", "max", "transform", "model"], outputs: ["modelChange", "sdChange"] }, { kind: "component", type: SdDatetime, selector: "sd-datetime", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "required", "disabled", "viewed", "showSeconds", "clearable", "inlineError", "hyperlink", "appearance", "floatLabel", "min", "minDate", "max", "maxDate", "transform", "model"], outputs: ["modelChange", "sdChange", "sdFocus"] }, { kind: "component", type: SdInput, selector: "sd-input", inputs: ["autoId", "name", "appearance", "floatLabel", "size", "form", "label", "helperText", "placeholder", "type", "mask", "hideInlineError", "blurOnEnter", "clearable", "required", "readonly", "disabled", "viewed", "minlength", "maxlength", "pattern", "patternErrorMessage", "validator", "inlineError", "hyperlink", "model"], outputs: ["modelChange", "sdChange", "sdFocus", "sdBlur", "keyupEnter", "cleared", "sdFocusForceBlur"] }, { kind: "component", type: SdSelect, selector: "sd-select", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "valueField", "displayField", "disabledField", "cacheChecksum", "limit", "hyperlink", "minWidthPanel", "hideInlineError", "required", "disabled", "viewed", "multiple", "showSelectAll", "clearable", "validator", "inlineError", "appearance", "floatLabel", "items", "model"], outputs: ["modelChange", "sdChange", "sdSelection"] }, { kind: "component", type: SdQueryInlineValueChip, selector: "sd-query-inline-value-chip", inputs: ["field", "operator", "value", "density", "showOperator", "autoId", "autofocus"], outputs: ["valueChange", "remove"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
468
527
  }
469
528
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdQueryBuildChip, decorators: [{
470
529
  type: Component,
471
- args: [{ selector: 'sd-query-build-chip', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [SdIcon, CommonModule, SdOperator, SdDate, SdDateRange, SdDatetime, SdInput, SdInputNumber, SdSelect, SdQueryInlineValueChip], template: "<!--\n In-progress chip (inline-mode token build flow).\n Seamless string/number branch keeps its own pill; everything else renders inside\n the dashed .c-token.c-token-building shell.\n-->\n@let _building = building();\n@let _autoId = autoId();\n\n@if (isSeamless()) {\n <sd-query-inline-value-chip\n [field]=\"_building.field\"\n [operator]=\"$any(_building.operator)\"\n [value]=\"_building.value ?? null\"\n [density]=\"density()\"\n [showOperator]=\"showOperator()\"\n [autoId]=\"_autoId\"\n [autofocus]=\"true\"\n (valueChange)=\"seamlessCommit.emit($event)\"\n (remove)=\"cancel.emit()\" />\n} @else {\n <div class=\"c-token c-token-building\">\n <span class=\"c-token-field\">{{ _building.field.label }}</span>\n\n @if (_building.step === 'operator') {\n <sd-operator\n #buildOperator\n class=\"c-token-op\"\n [operators]=\"allowedOperators()\"\n [model]=\"_building.operator\"\n (modelChange)=\"pickOperator.emit($any($event))\" />\n } @else {\n <!-- why: badge readonly cho operator \u0111\u00E3 ch\u1ECDn \u2014 v\u1EABn render <sd-operator>\n \u0111\u1EC3 gi\u1EEF th\u1ECB gi\u00E1c gi\u1ED1ng chip ho\u00E0n th\u00E0nh (icon + tooltip). -->\n <sd-operator\n class=\"c-token-op\"\n [operators]=\"[$any(_building.operator)]\"\n [model]=\"$any(_building.operator)\"\n [disabled]=\"true\" />\n\n @if (_building.field.type === 'values' || _building.field.type === 'lazy-values') {\n @let _bopt = $any(_building.field).option;\n <span class=\"c-token-value c-token-value-edit\">\n <sd-select #bPicker size=\"sm\" [viewed]=\"'inline'\" [clearable]=\"false\" minWidthPanel=\"300px\"\n [autoId]=\"_autoId\"\n [items]=\"_building.field.type === 'lazy-values' ? $any(_bopt).search : $any(_bopt.items)\"\n [valueField]=\"_bopt.valueField\"\n [displayField]=\"_bopt.displayField\"\n [multiple]=\"multiple()\"\n [model]=\"$any(_building.value)\"\n (sdChange)=\"commitValue.emit($event)\">\n <!-- why: build chip multi-select c\u0169ng c\u1EA7n \"head +N\" \u0111\u1EC3 g\u1ECDn (gi\u1ED1ng completed chip). -->\n <ng-template #sdValue let-selectedItems=\"selectedItems\">\n @if ((selectedItems?.length ?? 0) > 1) {\n <span class=\"c-token-multi-head\">{{ $any(selectedItems)[0][_bopt.displayField] }}</span> +{{ selectedItems.length - 1 }}\n } @else if ((selectedItems?.length ?? 0) === 1) {\n <span class=\"c-token-multi-head\">{{ $any(selectedItems)[0][_bopt.displayField] }}</span>\n }\n </ng-template>\n </sd-select>\n </span>\n } @else if (_building.field.type === 'date') {\n <span class=\"c-token-value c-token-value-edit\">\n @if (_building.operator === 'BETWEEN') {\n <!-- why: 1 sd-date-range thay 2 sd-date \u2014 g\u1ECDn h\u01A1n, h\u00E0nh vi th\u1ED1ng nh\u1EA5t v\u1EDBi completed chip. -->\n <sd-date-range #bPicker size=\"sm\" [viewed]=\"'inline'\" [clearable]=\"false\"\n [autoId]=\"_autoId\"\n [model]=\"$any(_building.value)\"\n (sdChange)=\"commitValue.emit($event)\"></sd-date-range>\n } @else {\n <sd-date #bPicker size=\"sm\" [viewed]=\"'inline'\" [clearable]=\"false\"\n [autoId]=\"_autoId\"\n [model]=\"$any(_building.value)\"\n (sdChange)=\"commitValue.emit($event)\"></sd-date>\n }\n </span>\n } @else if (_building.field.type === 'datetime') {\n <span class=\"c-token-value c-token-value-edit\">\n @if (_building.operator === 'BETWEEN') {\n <!-- why: datetime BETWEEN downgrade v\u1EC1 date-range (m\u1EA5t time precision; \u0111\u00E3 th\u1ED1ng nh\u1EA5t). -->\n <sd-date-range #bPicker size=\"sm\" [viewed]=\"'inline'\" [clearable]=\"false\"\n [autoId]=\"_autoId\"\n [model]=\"$any(_building.value)\"\n (sdChange)=\"commitValue.emit($event)\"></sd-date-range>\n } @else {\n <sd-datetime #bPicker size=\"sm\" [viewed]=\"'inline'\" [clearable]=\"false\"\n [autoId]=\"_autoId\"\n [model]=\"$any(_building.value)\"\n (sdChange)=\"commitValue.emit($event)\"></sd-datetime>\n }\n </span>\n } @else if (_building.field.type === 'boolean') {\n <!-- why: native button thay sd-button (tr\u00E1nh mat-button baseline 36px g\u00E2y tr\u00E0n).\n Pill nh\u1ECF v\u1EBD b\u1EB1ng CSS scoped trong chip \u2014 gi\u1EEF semantic colour qua class\n .c-bool-true / .c-bool-false. -->\n <span class=\"c-token-value c-token-value-edit c-bool-toggle\">\n <button\n type=\"button\"\n class=\"c-bool-btn c-bool-true\"\n [class.c-bool-active]=\"_building.value === true\"\n [attr.data-autoId]=\"_autoId + '-true'\"\n (click)=\"commitValue.emit(true)\">\n {{ $any(_building.field).trueLabel || 'C\u00F3' }}\n </button>\n <button\n type=\"button\"\n class=\"c-bool-btn c-bool-false\"\n [class.c-bool-active]=\"_building.value === false\"\n [attr.data-autoId]=\"_autoId + '-false'\"\n (click)=\"commitValue.emit(false)\">\n {{ $any(_building.field).falseLabel || 'Kh\u00F4ng' }}\n </button>\n </span>\n } @else {\n <!-- Fallback (e.g. unforeseen type) \u2014 generic sd-input wired via draft outputs. -->\n <span class=\"c-token-value c-token-value-edit\">\n <sd-input size=\"sm\"\n [autoId]=\"_autoId\"\n [model]=\"$any(_building.value)\"\n (sdChange)=\"draftChange.emit($event)\"\n (keyupEnter)=\"draftCommit.emit()\"></sd-input>\n </span>\n }\n }\n\n <button type=\"button\" class=\"c-token-remove\" aria-label=\"Cancel\" (click)=\"cancel.emit()\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n </div>\n}\n", styles: [":host{display:contents}.c-token{display:inline-flex;align-items:center;gap:4px;padding:0 0 0 12px;border:1px solid var(--sd-primary-light);border-radius:999px;background:var(--sd-primary-light);font-size:13px;color:#5f6368;white-space:nowrap;overflow:hidden}:host-context(.c-density-compact) .c-token{height:28px}:host-context(.c-density-comfortable) .c-token{height:32px}.c-token-field{font-weight:500;line-height:1}.c-token-op{display:inline-flex;align-items:center;color:inherit}.c-token-op ::ng-deep .c-op-trigger{padding:0;color:inherit}.c-token-op ::ng-deep .c-op-icon svg{width:14px;height:14px;display:block}.c-token-value{border:none;background:transparent;padding:0;font:inherit;font-weight:500;color:var(--sd-primary)}.c-token-value-edit{display:inline-flex;align-items:center;gap:4px;max-width:240px}.c-token-value-edit sd-input,.c-token-value-edit sd-input-number{display:inline-flex;max-width:140px}.c-token-value-edit sd-date-range{display:inline-flex;max-width:220px;min-width:0;flex:0 1 auto}.c-token-value-edit sd-select,.c-token-value-edit sd-date,.c-token-value-edit sd-datetime{display:inline-flex;align-items:center;line-height:1}.c-token ::ng-deep .mat-mdc-form-field{line-height:1}.c-token ::ng-deep .mat-mdc-text-field-wrapper,.c-token ::ng-deep .mat-mdc-form-field-flex{padding:0;min-height:0;background:transparent}.c-token ::ng-deep .mat-mdc-form-field-infix{padding:0;min-height:0;width:auto;border:0;display:inline-flex;align-items:center}.c-token ::ng-deep .mat-mdc-select-value,.c-token ::ng-deep .mat-mdc-input-element,.c-token ::ng-deep input{line-height:1;font-size:13px;padding:0;height:auto;color:inherit}.c-token ::ng-deep .mat-mdc-select-value{display:inline-flex;align-items:center;min-width:0;max-width:200px;overflow:hidden;text-overflow:ellipsis;color:var(--sd-primary);font-weight:500}.c-token ::ng-deep .sd-suffix-icon{display:none}.c-token-multi-head{display:inline-block;max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom}.c-token-building{border-style:dashed}.c-bool-toggle{display:inline-flex;align-items:center;gap:4px}.c-bool-btn{display:inline-flex;align-items:center;justify-content:center;height:18px;min-width:0;padding:0 8px;border:1px solid currentColor;border-radius:999px;background:transparent;font:inherit;font-size:11px;font-weight:500;line-height:1;cursor:pointer;white-space:nowrap;-webkit-user-select:none;user-select:none;transition:background .12s ease,color .12s ease}:host-context(.c-density-comfortable) .c-bool-btn{height:20px;padding:0 10px;font-size:12px}.c-bool-true{color:var(--sd-success)}.c-bool-false{color:var(--sd-warning)}.c-bool-active.c-bool-true{background:var(--sd-success);color:#fff;border-color:var(--sd-success)}.c-bool-active.c-bool-false{background:var(--sd-warning);color:#fff;border-color:var(--sd-warning)}.c-bool-btn:hover:not(.c-bool-active).c-bool-true{background:var(--sd-success-light)}.c-bool-btn:hover:not(.c-bool-active).c-bool-false{background:var(--sd-warning-light)}.c-token-remove{align-self:stretch;display:inline-flex;align-items:center;justify-content:center;border:none;border-left:1px solid rgba(0,0,0,.04);border-radius:0;background:transparent;cursor:pointer;color:inherit}:host-context(.c-density-compact) .c-token-remove{width:26px}:host-context(.c-density-comfortable) .c-token-remove{width:28px}.c-token-remove sd-icon{font-size:14px;width:14px;height:14px}.c-token-remove:hover{background:#0000000d}\n"] }]
530
+ args: [{ selector: 'sd-query-build-chip', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [SdIcon, CommonModule, SdOperator, SdDate, SdDateRange, SdDatetime, SdInput, SdInputNumber, SdSelect, SdQueryInlineValueChip], template: "<!--\n In-progress chip (inline-mode token build flow).\n Seamless string/number branch keeps its own pill; everything else renders inside\n the dashed .c-token.c-token-building shell.\n-->\n@let _building = building();\n@let _autoId = autoId();\n\n@if (isSeamless()) {\n <sd-query-inline-value-chip\n [field]=\"_building.field\"\n [operator]=\"$any(_building.operator)\"\n [value]=\"_building.value ?? null\"\n [density]=\"density()\"\n [showOperator]=\"showOperator()\"\n [autoId]=\"_autoId\"\n [autofocus]=\"true\"\n (valueChange)=\"seamlessCommit.emit($event)\"\n (remove)=\"cancel.emit()\" />\n} @else {\n <div class=\"c-token c-token-building\">\n <span class=\"c-token-field\">{{ _building.field.label }}</span>\n\n @if (_building.step === 'operator') {\n <sd-operator\n #buildOperator\n class=\"c-token-op\"\n [operators]=\"allowedOperators()\"\n [model]=\"_building.operator\"\n (modelChange)=\"pickOperator.emit($any($event))\" />\n } @else {\n <!-- why: badge readonly cho operator \u0111\u00E3 ch\u1ECDn \u2014 v\u1EABn render <sd-operator>\n \u0111\u1EC3 gi\u1EEF th\u1ECB gi\u00E1c gi\u1ED1ng chip ho\u00E0n th\u00E0nh (icon + tooltip). -->\n <sd-operator\n class=\"c-token-op\"\n [operators]=\"[$any(_building.operator)]\"\n [model]=\"$any(_building.operator)\"\n [disabled]=\"true\" />\n\n @if (_building.field.type === 'values' || _building.field.type === 'lazy-values') {\n @let _bopt = $any(_building.field).option;\n <span class=\"c-token-value c-token-value-edit\">\n <sd-select #bPicker size=\"sm\" [viewed]=\"'inline'\" [clearable]=\"false\" minWidthPanel=\"300px\"\n [autoId]=\"_autoId\"\n [items]=\"_building.field.type === 'lazy-values' ? $any(_bopt).search : $any(_bopt.items)\"\n [valueField]=\"_bopt.valueField\"\n [displayField]=\"_bopt.displayField\"\n [multiple]=\"multiple()\"\n [model]=\"$any(_building.value)\"\n (sdChange)=\"commitValue.emit($event)\">\n <!-- why: build chip multi-select c\u0169ng c\u1EA7n \"head +N\" \u0111\u1EC3 g\u1ECDn (gi\u1ED1ng completed chip). -->\n <ng-template #sdValue let-selectedItems=\"selectedItems\">\n @if ((selectedItems?.length ?? 0) > 1) {\n <span class=\"c-token-multi-head\">{{ $any(selectedItems)[0][_bopt.displayField] }}</span> +{{ selectedItems.length - 1 }}\n } @else if ((selectedItems?.length ?? 0) === 1) {\n <span class=\"c-token-multi-head\">{{ $any(selectedItems)[0][_bopt.displayField] }}</span>\n }\n </ng-template>\n </sd-select>\n </span>\n } @else if (_building.field.type === 'date') {\n <span class=\"c-token-value c-token-value-edit\">\n @if (_building.operator === 'BETWEEN') {\n <!-- why: 1 sd-date-range thay 2 sd-date \u2014 g\u1ECDn h\u01A1n, h\u00E0nh vi th\u1ED1ng nh\u1EA5t v\u1EDBi completed chip. -->\n <sd-date-range #bPicker size=\"sm\" [viewed]=\"'inline'\" [clearable]=\"false\"\n [autoId]=\"_autoId\"\n [model]=\"$any(_building.value)\"\n (sdChange)=\"commitValue.emit($event)\"></sd-date-range>\n } @else {\n <sd-date #bPicker size=\"sm\" [viewed]=\"'inline'\" [clearable]=\"false\"\n [autoId]=\"_autoId\"\n [model]=\"$any(_building.value)\"\n (sdChange)=\"commitValue.emit($event)\"></sd-date>\n }\n </span>\n } @else if (_building.field.type === 'datetime') {\n <span class=\"c-token-value c-token-value-edit\">\n @if (_building.operator === 'BETWEEN') {\n <!-- why: datetime BETWEEN downgrade v\u1EC1 date-range (m\u1EA5t time precision; \u0111\u00E3 th\u1ED1ng nh\u1EA5t). -->\n <sd-date-range #bPicker size=\"sm\" [viewed]=\"'inline'\" [clearable]=\"false\"\n [autoId]=\"_autoId\"\n [model]=\"$any(_building.value)\"\n (sdChange)=\"commitValue.emit($event)\"></sd-date-range>\n } @else {\n <sd-datetime #bPicker size=\"sm\" [viewed]=\"'inline'\" [clearable]=\"false\"\n [autoId]=\"_autoId\"\n [model]=\"$any(_building.value)\"\n (sdChange)=\"commitValue.emit($event)\"></sd-datetime>\n }\n </span>\n } @else if (_building.field.type === 'boolean') {\n <!-- why: native button thay sd-button (tr\u00E1nh mat-button baseline 36px g\u00E2y tr\u00E0n).\n Pill nh\u1ECF v\u1EBD b\u1EB1ng CSS scoped trong chip \u2014 gi\u1EEF semantic colour qua class\n .c-bool-true / .c-bool-false. -->\n <span class=\"c-token-value c-token-value-edit c-bool-toggle\">\n <button\n type=\"button\"\n class=\"c-bool-btn c-bool-true\"\n [class.c-bool-active]=\"_building.value === true\"\n [attr.data-autoId]=\"_autoId + '-true'\"\n (click)=\"commitValue.emit(true)\">\n {{ $any(_building.field).trueLabel || booleanTrueLabel() }}\n </button>\n <button\n type=\"button\"\n class=\"c-bool-btn c-bool-false\"\n [class.c-bool-active]=\"_building.value === false\"\n [attr.data-autoId]=\"_autoId + '-false'\"\n (click)=\"commitValue.emit(false)\">\n {{ $any(_building.field).falseLabel || booleanFalseLabel() }}\n </button>\n </span>\n } @else {\n <!-- Fallback (e.g. unforeseen type) \u2014 generic sd-input wired via draft outputs. -->\n <span class=\"c-token-value c-token-value-edit\">\n <sd-input size=\"sm\"\n [autoId]=\"_autoId\"\n [model]=\"$any(_building.value)\"\n (sdChange)=\"draftChange.emit($event)\"\n (keyupEnter)=\"draftCommit.emit()\"></sd-input>\n </span>\n }\n }\n\n <button type=\"button\" class=\"c-token-remove\" aria-label=\"Cancel\" (click)=\"cancel.emit()\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n </div>\n}\n", styles: [":host{display:contents}.c-token{display:inline-flex;align-items:center;gap:4px;padding:0 0 0 12px;border:1px solid var(--sd-primary-light);border-radius:999px;background:var(--sd-primary-light);font-size:13px;color:#5f6368;white-space:nowrap;overflow:hidden}:host-context(.c-density-compact) .c-token{height:28px}:host-context(.c-density-comfortable) .c-token{height:32px}.c-token-field{font-weight:500;line-height:1}.c-token-op{display:inline-flex;align-items:center;color:inherit}.c-token-op ::ng-deep .c-op-trigger{padding:0;color:inherit}.c-token-op ::ng-deep .c-op-icon svg{width:14px;height:14px;display:block}.c-token-value{border:none;background:transparent;padding:0;font:inherit;font-weight:500;color:var(--sd-primary)}.c-token-value-edit{display:inline-flex;align-items:center;gap:4px;max-width:240px}.c-token-value-edit sd-input,.c-token-value-edit sd-input-number{display:inline-flex;max-width:140px}.c-token-value-edit sd-date-range{display:inline-flex;max-width:220px;min-width:0;flex:0 1 auto}.c-token-value-edit sd-select,.c-token-value-edit sd-date,.c-token-value-edit sd-datetime{display:inline-flex;align-items:center;line-height:1}.c-token ::ng-deep .mat-mdc-form-field{line-height:1}.c-token ::ng-deep .mat-mdc-text-field-wrapper,.c-token ::ng-deep .mat-mdc-form-field-flex{padding:0;min-height:0;background:transparent}.c-token ::ng-deep .mat-mdc-form-field-infix{padding:0;min-height:0;width:auto;border:0;display:inline-flex;align-items:center}.c-token ::ng-deep .mat-mdc-select-value,.c-token ::ng-deep .mat-mdc-input-element,.c-token ::ng-deep input{line-height:1;font-size:13px;padding:0;height:auto;color:inherit}.c-token ::ng-deep .mat-mdc-select-value{display:inline-flex;align-items:center;min-width:0;max-width:200px;overflow:hidden;text-overflow:ellipsis;color:var(--sd-primary);font-weight:500}.c-token ::ng-deep .sd-suffix-icon{display:none}.c-token-multi-head{display:inline-block;max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom}.c-token-building{border-style:dashed}.c-bool-toggle{display:inline-flex;align-items:center;gap:4px}.c-bool-btn{display:inline-flex;align-items:center;justify-content:center;height:18px;min-width:0;padding:0 8px;border:1px solid currentColor;border-radius:999px;background:transparent;font:inherit;font-size:11px;font-weight:500;line-height:1;cursor:pointer;white-space:nowrap;-webkit-user-select:none;user-select:none;transition:background .12s ease,color .12s ease}:host-context(.c-density-comfortable) .c-bool-btn{height:20px;padding:0 10px;font-size:12px}.c-bool-true{color:var(--sd-success)}.c-bool-false{color:var(--sd-warning)}.c-bool-active.c-bool-true{background:var(--sd-success);color:#fff;border-color:var(--sd-success)}.c-bool-active.c-bool-false{background:var(--sd-warning);color:#fff;border-color:var(--sd-warning)}.c-bool-btn:hover:not(.c-bool-active).c-bool-true{background:var(--sd-success-light)}.c-bool-btn:hover:not(.c-bool-active).c-bool-false{background:var(--sd-warning-light)}.c-token-remove{align-self:stretch;display:inline-flex;align-items:center;justify-content:center;border:none;border-left:1px solid rgba(0,0,0,.04);border-radius:0;background:transparent;cursor:pointer;color:inherit}:host-context(.c-density-compact) .c-token-remove{width:26px}:host-context(.c-density-comfortable) .c-token-remove{width:28px}.c-token-remove sd-icon{font-size:14px;width:14px;height:14px}.c-token-remove:hover{background:#0000000d}\n"] }]
472
531
  }], propDecorators: { building: [{ type: i0.Input, args: [{ isSignal: true, alias: "building", required: true }] }], density: [{ type: i0.Input, args: [{ isSignal: true, alias: "density", required: false }] }], allowedOperators: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowedOperators", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], showOperator: [{ type: i0.Input, args: [{ isSignal: true, alias: "showOperator", required: false }] }], autoId: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoId", required: false }] }], pickOperator: [{ type: i0.Output, args: ["pickOperator"] }], commitValue: [{ type: i0.Output, args: ["commitValue"] }], cancel: [{ type: i0.Output, args: ["cancel"] }], seamlessCommit: [{ type: i0.Output, args: ["seamlessCommit"] }], draftChange: [{ type: i0.Output, args: ["draftChange"] }], draftCommit: [{ type: i0.Output, args: ["draftCommit"] }], opMenu: [{ type: i0.ViewChild, args: ['buildOperator', { isSignal: true }] }], picker: [{ type: i0.ViewChild, args: ['bPicker', { isSignal: true }] }] } });
473
532
 
474
533
  /**
@@ -486,6 +545,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
486
545
  * - `(swapField)` output — emitted when the nested field switcher picks a new field
487
546
  */
488
547
  class SdQueryChipPopover {
548
+ #i18n = inject(I18nService);
549
+ swapFieldTooltip = computed(() => this.#i18n.t('core.component.query-bar.swap-field'), ...(ngDevMode ? [{ debugName: "swapFieldTooltip" }] : /* istanbul ignore next */ []));
550
+ fromPlaceholder = computed(() => this.#i18n.t('core.component.query-bar.placeholder.from-number'), ...(ngDevMode ? [{ debugName: "fromPlaceholder" }] : /* istanbul ignore next */ []));
551
+ toPlaceholder = computed(() => this.#i18n.t('core.component.query-bar.placeholder.to-number'), ...(ngDevMode ? [{ debugName: "toPlaceholder" }] : /* istanbul ignore next */ []));
552
+ booleanTrueLabel = computed(() => this.#i18n.t('core.component.query-bar.boolean.true'), ...(ngDevMode ? [{ debugName: "booleanTrueLabel" }] : /* istanbul ignore next */ []));
553
+ booleanFalseLabel = computed(() => this.#i18n.t('core.component.query-bar.boolean.false'), ...(ngDevMode ? [{ debugName: "booleanFalseLabel" }] : /* istanbul ignore next */ []));
554
+ selectValuePlaceholder = computed(() => this.#i18n.t('core.component.query-bar.placeholder.select-value'), ...(ngDevMode ? [{ debugName: "selectValuePlaceholder" }] : /* istanbul ignore next */ []));
555
+ enterValuePlaceholder = computed(() => this.#i18n.t('core.component.query-bar.placeholder.enter-value'), ...(ngDevMode ? [{ debugName: "enterValuePlaceholder" }] : /* istanbul ignore next */ []));
556
+ loadingValuesLabel = computed(() => this.#i18n.t('core.component.query-bar.loading-values'), ...(ngDevMode ? [{ debugName: "loadingValuesLabel" }] : /* istanbul ignore next */ []));
489
557
  // ---------------------------------------------------------------------------
490
558
  // Inputs
491
559
  // ---------------------------------------------------------------------------
@@ -691,7 +759,7 @@ class SdQueryChipPopover {
691
759
  }
692
760
  }
693
761
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdQueryChipPopover, deps: [], target: i0.ɵɵFactoryTarget.Component });
694
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SdQueryChipPopover, isStandalone: true, selector: "sd-query-chip-popover", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, chipIndex: { classPropertyName: "chipIndex", publicName: "chipIndex", isSignal: true, isRequired: false, transformFunction: null }, autoIdBase: { classPropertyName: "autoIdBase", publicName: "autoIdBase", isSignal: true, isRequired: false, transformFunction: null }, switchPickerMenu: { classPropertyName: "switchPickerMenu", publicName: "switchPickerMenu", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { commit: "commit", swapField: "swapField" }, viewQueries: [{ propertyName: "menu", first: true, predicate: ["menu"], descendants: true, isSignal: true }], ngImport: i0, template: "<!--\n Chip popover \u2014 operator + value editor for the popover-mode chip.\n Visual layout mirrors the previous inline `<mat-menu #chipPopover>` in query-bar.\n Body branches on operator + field.type \u2014 8 paths preserved exactly.\n-->\n<mat-menu #menu=\"matMenu\" class=\"c-chip-popover\" xPosition=\"after\" yPosition=\"below\" (closed)=\"onMenuClosed()\">\n <ng-template matMenuContent>\n @let _field = field();\n @let _editingOperator = editingOperator();\n @let _editingValue = editingValue();\n @let _editingOptions = editingOptions();\n @let _editingOptionsLoading = editingOptionsLoading();\n @let _switchPickerMenu = switchPickerMenu();\n\n @if (_field) {\n <div class=\"c-pop-header\" (click)=\"$event.stopPropagation()\">\n <sd-icon [name]=\"iconFor(_field)\"></sd-icon>\n <!-- why: nested mat-menu trigger to swap the chip's field \u2014 parent provides\n the field-picker MatMenu via [switchPickerMenu]. -->\n @if (_switchPickerMenu) {\n <button\n type=\"button\"\n class=\"c-pop-header-field\"\n [matMenuTriggerFor]=\"_switchPickerMenu\"\n matTooltip=\"\u0110\u1ED5i field\">\n <span>{{ _field.label }}</span>\n <sd-icon name=\"unfold_more\"></sd-icon>\n </button>\n } @else {\n <span class=\"c-pop-header-field\">{{ _field.label }}</span>\n }\n <!-- Operator selector (right-aligned). Disabled when field has \u22641 operator. -->\n <sd-operator\n class=\"c-pop-header-operator\"\n [autoId]=\"chipAutoId('operator')\"\n [operators]=\"allowedOperators()\"\n [model]=\"_editingOperator\"\n [disabled]=\"!showOperatorSelector()\"\n (modelChange)=\"onEditingOperatorChange($any($event))\" />\n </div>\n\n <div class=\"c-pop-body\" (click)=\"$event.stopPropagation()\">\n @if (!isNoDataOperator(_editingOperator)) {\n @if (_editingOperator === 'BETWEEN') {\n @if (_field.type === 'date') {\n <div class=\"c-pop-between\">\n <sd-date size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-from')\"\n placeholder=\"T\u1EEB\"\n [model]=\"$any(_editingValue)?.from\"\n (sdChange)=\"onEditingRangeFrom($event)\">\n </sd-date>\n <sd-date size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-to')\"\n placeholder=\"\u0110\u1EBFn\"\n [model]=\"$any(_editingValue)?.to\"\n (sdChange)=\"onEditingRangeTo($event)\">\n </sd-date>\n </div>\n } @else if (_field.type === 'datetime') {\n <div class=\"c-pop-between\">\n <sd-datetime size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-from')\"\n placeholder=\"T\u1EEB\"\n [model]=\"$any(_editingValue)?.from\"\n (sdChange)=\"onEditingRangeFrom($event)\">\n </sd-datetime>\n <sd-datetime size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-to')\"\n placeholder=\"\u0110\u1EBFn\"\n [model]=\"$any(_editingValue)?.to\"\n (sdChange)=\"onEditingRangeTo($event)\">\n </sd-datetime>\n </div>\n } @else {\n <div class=\"c-pop-between\">\n <sd-input-number size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-from')\"\n placeholder=\"T\u1EEB\"\n [model]=\"$any(_editingValue)?.from\"\n (sdChange)=\"onEditingRangeFrom($event)\">\n </sd-input-number>\n <sd-input-number size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-to')\"\n placeholder=\"\u0110\u1EBFn\"\n [model]=\"$any(_editingValue)?.to\"\n (sdChange)=\"onEditingRangeTo($event)\">\n </sd-input-number>\n </div>\n }\n } @else if (_field.type === 'boolean') {\n <div class=\"c-pop-boolean c-bool-toggle\">\n <sd-button\n [autoId]=\"chipAutoId('value-true')\"\n [type]=\"_editingValue === true ? 'fill' : 'outline'\"\n color=\"success\"\n [title]=\"$any(_field).trueLabel || 'C\u00F3'\"\n (click)=\"onEditingValueInput(true)\">\n </sd-button>\n <sd-button\n [autoId]=\"chipAutoId('value-false')\"\n [type]=\"_editingValue === false ? 'fill' : 'outline'\"\n color=\"warning\"\n [title]=\"$any(_field).falseLabel || 'Kh\u00F4ng'\"\n (click)=\"onEditingValueInput(false)\">\n </sd-button>\n </div>\n } @else if (_field.type === 'values' || _field.type === 'lazy-values') {\n @let _opt = $any(_field).option;\n <sd-select size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value')\"\n placeholder=\"Ch\u1ECDn gi\u00E1 tr\u1ECB\"\n [items]=\"_editingOptions\"\n [valueField]=\"_opt.valueField\"\n [displayField]=\"_opt.displayField\"\n [multiple]=\"multiple()\"\n [model]=\"$any(_editingValue)\"\n (sdChange)=\"onEditingValueInput($event)\">\n </sd-select>\n @if (_editingOptionsLoading) {\n <div class=\"c-pop-values-loading\">\u0110ang t\u1EA3i...</div>\n }\n } @else if (_field.type === 'date') {\n <sd-date size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value')\"\n placeholder=\"Ch\u1ECDn gi\u00E1 tr\u1ECB\"\n [model]=\"$any(_editingValue)\"\n (sdChange)=\"onEditingValueInput($event)\">\n </sd-date>\n } @else if (_field.type === 'datetime') {\n <sd-datetime size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value')\"\n placeholder=\"Ch\u1ECDn gi\u00E1 tr\u1ECB\"\n [model]=\"$any(_editingValue)\"\n (sdChange)=\"onEditingValueInput($event)\">\n </sd-datetime>\n } @else if (_field.type === 'number') {\n <sd-input-number size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value')\"\n placeholder=\"Nh\u1EADp gi\u00E1 tr\u1ECB\"\n [model]=\"$any(_editingValue)\"\n (sdChange)=\"onEditingValueInput($event)\">\n </sd-input-number>\n } @else {\n <sd-input size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value')\"\n placeholder=\"Nh\u1EADp gi\u00E1 tr\u1ECB\"\n [model]=\"$any(_editingValue)\"\n (sdChange)=\"onEditingValueInput($event)\">\n </sd-input>\n }\n }\n </div>\n }\n </ng-template>\n</mat-menu>\n", styles: [":host{display:contents}::ng-deep .mat-mdc-menu-panel.c-chip-popover{min-width:280px;max-width:320px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header{display:flex;align-items:center;gap:8px;padding:10px 12px;border-bottom:1px solid #e2e4e7;font-size:13px;font-weight:500;color:#212121}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header sd-icon{font-size:16px;width:16px;height:16px;color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header .c-pop-header-operator{margin-left:auto}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field{display:inline-flex;align-items:center;gap:4px;padding:2px 4px 2px 6px;margin-left:-4px;background:transparent;border:1px solid transparent;border-radius:4px;font:inherit;font-weight:500;color:#212121;cursor:pointer;transition:background-color .12s ease,border-color .12s ease}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field sd-icon{font-size:14px!important;width:14px!important;height:14px!important;color:#7a7a7a!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field:hover{background:#f5f6f7;border-color:#e2e4e7}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-body{padding:10px 12px;display:flex;flex-direction:column;gap:12px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-between{display:grid;grid-template-columns:1fr 1fr;gap:8px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-boolean{display:flex;gap:8px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-boolean button{flex:1;height:32px;border:1px solid #d6d8db;border-radius:6px;background:#fff;font:inherit;font-size:13px;cursor:pointer}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values-loading{padding:10px 8px;font-size:12px;color:#7a7a7a}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-bool-toggle{display:inline-flex;align-items:center;gap:4px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-bool-toggle .mdc-button,::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-bool-toggle button[mat-button],::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-bool-toggle button[mat-stroked-button],::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-bool-toggle button[mat-flat-button]{border-radius:999px;min-width:0;padding:0 12px}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i1.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "directive", type: i1.MatMenuContent, selector: "ng-template[matMenuContent]" }, { kind: "directive", type: i1.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: SdButton, selector: "sd-button", inputs: ["autoId", "type", "color", "size", "fontSet", "title", "width", "tooltip", "prefixIcon", "suffixIcon", "disabled", "loading", "block", "htmlType"], outputs: ["click"] }, { kind: "component", type: SdOperator, selector: "sd-operator", inputs: ["model", "operators", "disabled", "autoId"], outputs: ["modelChange"] }, { kind: "component", type: SdSelect, selector: "sd-select", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "valueField", "displayField", "disabledField", "cacheChecksum", "limit", "hyperlink", "minWidthPanel", "hideInlineError", "required", "disabled", "viewed", "multiple", "clearable", "validator", "inlineError", "appearance", "floatLabel", "items", "model"], outputs: ["modelChange", "sdChange", "sdSelection"] }, { kind: "component", type: SdInput, selector: "sd-input", inputs: ["autoId", "name", "appearance", "floatLabel", "size", "form", "label", "helperText", "placeholder", "type", "mask", "hideInlineError", "blurOnEnter", "clearable", "required", "readonly", "disabled", "viewed", "minlength", "maxlength", "pattern", "patternErrorMessage", "validator", "inlineError", "hyperlink", "model"], outputs: ["modelChange", "sdChange", "sdFocus", "sdBlur", "keyupEnter", "cleared", "sdFocusForceBlur"] }, { kind: "component", type: SdInputNumber, selector: "sd-input-number", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "blurOnEnter", "clearable", "required", "readonly", "disabled", "viewed", "type", "precision", "format", "min", "max", "validator", "inlineError", "hyperlink", "appearance", "floatLabel", "model"], outputs: ["modelChange", "sdChange", "sdFocus", "sdBlur", "keyupEnter", "cleared", "sdFocusForceBlur"] }, { kind: "component", type: SdDate, selector: "sd-date", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "required", "disabled", "viewed", "clearable", "inlineError", "hyperlink", "appearance", "floatLabel", "min", "minDate", "max", "maxDate", "model"], outputs: ["modelChange", "sdChange", "sdFocus"] }, { kind: "component", type: SdDatetime, selector: "sd-datetime", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "required", "disabled", "viewed", "showSeconds", "clearable", "inlineError", "hyperlink", "appearance", "floatLabel", "min", "minDate", "max", "maxDate", "model"], outputs: ["modelChange", "sdChange", "sdFocus"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
762
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SdQueryChipPopover, isStandalone: true, selector: "sd-query-chip-popover", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, chipIndex: { classPropertyName: "chipIndex", publicName: "chipIndex", isSignal: true, isRequired: false, transformFunction: null }, autoIdBase: { classPropertyName: "autoIdBase", publicName: "autoIdBase", isSignal: true, isRequired: false, transformFunction: null }, switchPickerMenu: { classPropertyName: "switchPickerMenu", publicName: "switchPickerMenu", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { commit: "commit", swapField: "swapField" }, viewQueries: [{ propertyName: "menu", first: true, predicate: ["menu"], descendants: true, isSignal: true }], ngImport: i0, template: "<!--\n Chip popover \u2014 operator + value editor for the popover-mode chip.\n Visual layout mirrors the previous inline `<mat-menu #chipPopover>` in query-bar.\n Body branches on operator + field.type \u2014 8 paths preserved exactly.\n-->\n<mat-menu #menu=\"matMenu\" class=\"c-chip-popover\" xPosition=\"after\" yPosition=\"below\" (closed)=\"onMenuClosed()\">\n <ng-template matMenuContent>\n @let _field = field();\n @let _editingOperator = editingOperator();\n @let _editingValue = editingValue();\n @let _editingOptions = editingOptions();\n @let _editingOptionsLoading = editingOptionsLoading();\n @let _switchPickerMenu = switchPickerMenu();\n\n @if (_field) {\n <!-- why: header/body c\u1EE7a popover l\u00E0 NH\u00D3M control (role=\"group\"), kh\u00F4ng ph\u1EA3i n\u00FAt \u2014 n\u00EAn ch\u00FAng\n KH\u00D4NG \u0111\u01B0\u1EE3c nh\u1EADn tabindex; tab ph\u1EA3i \u0111i th\u1EB3ng v\u00E0o c\u00E1c control b\u00EAn trong. Th\u00EAm ch\u1EB7n Enter\n b\u00EAn c\u1EA1nh ch\u1EB7n click: c\u1EA3 hai ch\u1EC9 \u0111\u1EC3 mat-menu kh\u00F4ng t\u1EF1 \u0111\u00F3ng khi ng\u01B0\u1EDDi d\u00F9ng thao t\u00E1c trong\n popover; Escape v\u00E0 ph\u00EDm m\u0169i t\u00EAn v\u1EABn b\u1ECDt l\u00EAn \u0111\u1EC3 menu \u0111\u00F3ng/\u0111i\u1EC1u h\u01B0\u1EDBng b\u00ECnh th\u01B0\u1EDDng. -->\n <div class=\"c-pop-header\" role=\"group\" (click)=\"$event.stopPropagation()\" (keydown.enter)=\"$event.stopPropagation()\">\n <sd-icon [name]=\"iconFor(_field)\"></sd-icon>\n <!-- why: nested mat-menu trigger to swap the chip's field \u2014 parent provides\n the field-picker MatMenu via [switchPickerMenu]. -->\n @if (_switchPickerMenu) {\n <button\n type=\"button\"\n class=\"c-pop-header-field\"\n [matMenuTriggerFor]=\"_switchPickerMenu\"\n [matTooltip]=\"swapFieldTooltip()\">\n <span>{{ _field.label }}</span>\n <sd-icon name=\"unfold_more\"></sd-icon>\n </button>\n } @else {\n <span class=\"c-pop-header-field\">{{ _field.label }}</span>\n }\n <!-- Operator selector (right-aligned). Disabled when field has \u22641 operator. -->\n <sd-operator\n class=\"c-pop-header-operator\"\n [autoId]=\"chipAutoId('operator')\"\n [operators]=\"allowedOperators()\"\n [model]=\"_editingOperator\"\n [disabled]=\"!showOperatorSelector()\"\n (modelChange)=\"onEditingOperatorChange($any($event))\" />\n </div>\n\n <div class=\"c-pop-body\" role=\"group\" (click)=\"$event.stopPropagation()\" (keydown.enter)=\"$event.stopPropagation()\">\n @if (!isNoDataOperator(_editingOperator)) {\n @if (_editingOperator === 'BETWEEN') {\n @if (_field.type === 'date') {\n <div class=\"c-pop-between\">\n <sd-date size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-from')\"\n [placeholder]=\"fromPlaceholder()\"\n [model]=\"$any(_editingValue)?.from\"\n (sdChange)=\"onEditingRangeFrom($event)\">\n </sd-date>\n <sd-date size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-to')\"\n [placeholder]=\"toPlaceholder()\"\n [model]=\"$any(_editingValue)?.to\"\n (sdChange)=\"onEditingRangeTo($event)\">\n </sd-date>\n </div>\n } @else if (_field.type === 'datetime') {\n <div class=\"c-pop-between\">\n <sd-datetime size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-from')\"\n [placeholder]=\"fromPlaceholder()\"\n [model]=\"$any(_editingValue)?.from\"\n (sdChange)=\"onEditingRangeFrom($event)\">\n </sd-datetime>\n <sd-datetime size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-to')\"\n [placeholder]=\"toPlaceholder()\"\n [model]=\"$any(_editingValue)?.to\"\n (sdChange)=\"onEditingRangeTo($event)\">\n </sd-datetime>\n </div>\n } @else {\n <div class=\"c-pop-between\">\n <sd-input-number size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-from')\"\n [placeholder]=\"fromPlaceholder()\"\n [model]=\"$any(_editingValue)?.from\"\n (sdChange)=\"onEditingRangeFrom($event)\">\n </sd-input-number>\n <sd-input-number size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-to')\"\n [placeholder]=\"toPlaceholder()\"\n [model]=\"$any(_editingValue)?.to\"\n (sdChange)=\"onEditingRangeTo($event)\">\n </sd-input-number>\n </div>\n }\n } @else if (_field.type === 'boolean') {\n <div class=\"c-pop-boolean c-bool-toggle\">\n <sd-button\n [autoId]=\"chipAutoId('value-true')\"\n [type]=\"_editingValue === true ? 'fill' : 'outline'\"\n color=\"success\"\n [title]=\"$any(_field).trueLabel || booleanTrueLabel()\"\n (click)=\"onEditingValueInput(true)\">\n </sd-button>\n <sd-button\n [autoId]=\"chipAutoId('value-false')\"\n [type]=\"_editingValue === false ? 'fill' : 'outline'\"\n color=\"warning\"\n [title]=\"$any(_field).falseLabel || booleanFalseLabel()\"\n (click)=\"onEditingValueInput(false)\">\n </sd-button>\n </div>\n } @else if (_field.type === 'values' || _field.type === 'lazy-values') {\n @let _opt = $any(_field).option;\n <sd-select size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value')\"\n [placeholder]=\"selectValuePlaceholder()\"\n [items]=\"_editingOptions\"\n [valueField]=\"_opt.valueField\"\n [displayField]=\"_opt.displayField\"\n [multiple]=\"multiple()\"\n [model]=\"$any(_editingValue)\"\n (sdChange)=\"onEditingValueInput($event)\">\n </sd-select>\n @if (_editingOptionsLoading) {\n <div class=\"c-pop-values-loading\">{{ loadingValuesLabel() }}</div>\n }\n } @else if (_field.type === 'date') {\n <sd-date size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value')\"\n [placeholder]=\"selectValuePlaceholder()\"\n [model]=\"$any(_editingValue)\"\n (sdChange)=\"onEditingValueInput($event)\">\n </sd-date>\n } @else if (_field.type === 'datetime') {\n <sd-datetime size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value')\"\n [placeholder]=\"selectValuePlaceholder()\"\n [model]=\"$any(_editingValue)\"\n (sdChange)=\"onEditingValueInput($event)\">\n </sd-datetime>\n } @else if (_field.type === 'number') {\n <sd-input-number size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value')\"\n [placeholder]=\"enterValuePlaceholder()\"\n [model]=\"$any(_editingValue)\"\n (sdChange)=\"onEditingValueInput($event)\">\n </sd-input-number>\n } @else {\n <sd-input size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value')\"\n [placeholder]=\"enterValuePlaceholder()\"\n [model]=\"$any(_editingValue)\"\n (sdChange)=\"onEditingValueInput($event)\">\n </sd-input>\n }\n }\n </div>\n }\n </ng-template>\n</mat-menu>\n", styles: [":host{display:contents}::ng-deep .mat-mdc-menu-panel.c-chip-popover{min-width:280px;max-width:320px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header{display:flex;align-items:center;gap:8px;padding:10px 12px;border-bottom:1px solid #e2e4e7;font-size:13px;font-weight:500;color:#212121}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header sd-icon{font-size:16px;width:16px;height:16px;color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header .c-pop-header-operator{margin-left:auto}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field{display:inline-flex;align-items:center;gap:4px;padding:2px 4px 2px 6px;margin-left:-4px;background:transparent;border:1px solid transparent;border-radius:4px;font:inherit;font-weight:500;color:#212121;cursor:pointer;transition:background-color .12s ease,border-color .12s ease}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field sd-icon{font-size:14px!important;width:14px!important;height:14px!important;color:#7a7a7a!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field:hover{background:#f5f6f7;border-color:#e2e4e7}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-body{padding:10px 12px;display:flex;flex-direction:column;gap:12px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-between{display:grid;grid-template-columns:1fr 1fr;gap:8px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-boolean{display:flex;gap:8px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-boolean button{flex:1;height:32px;border:1px solid #d6d8db;border-radius:6px;background:#fff;font:inherit;font-size:13px;cursor:pointer}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values-loading{padding:10px 8px;font-size:12px;color:#7a7a7a}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-bool-toggle{display:inline-flex;align-items:center;gap:4px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-bool-toggle .mdc-button,::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-bool-toggle button[mat-button],::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-bool-toggle button[mat-stroked-button],::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-bool-toggle button[mat-flat-button]{border-radius:999px;min-width:0;padding:0 12px}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i1.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "directive", type: i1.MatMenuContent, selector: "ng-template[matMenuContent]" }, { kind: "directive", type: i1.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: SdButton, selector: "sd-button", inputs: ["autoId", "type", "color", "size", "fontSet", "title", "width", "tooltip", "prefixIcon", "suffixIcon", "disabled", "loading", "block", "htmlType"], outputs: ["click"] }, { kind: "component", type: SdOperator, selector: "sd-operator", inputs: ["model", "operators", "disabled", "autoId"], outputs: ["modelChange"] }, { kind: "component", type: SdSelect, selector: "sd-select", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "valueField", "displayField", "disabledField", "cacheChecksum", "limit", "hyperlink", "minWidthPanel", "hideInlineError", "required", "disabled", "viewed", "multiple", "showSelectAll", "clearable", "validator", "inlineError", "appearance", "floatLabel", "items", "model"], outputs: ["modelChange", "sdChange", "sdSelection"] }, { kind: "component", type: SdInput, selector: "sd-input", inputs: ["autoId", "name", "appearance", "floatLabel", "size", "form", "label", "helperText", "placeholder", "type", "mask", "hideInlineError", "blurOnEnter", "clearable", "required", "readonly", "disabled", "viewed", "minlength", "maxlength", "pattern", "patternErrorMessage", "validator", "inlineError", "hyperlink", "model"], outputs: ["modelChange", "sdChange", "sdFocus", "sdBlur", "keyupEnter", "cleared", "sdFocusForceBlur"] }, { kind: "component", type: SdInputNumber, selector: "sd-input-number", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "blurOnEnter", "clearable", "required", "readonly", "disabled", "viewed", "type", "precision", "format", "min", "max", "validator", "inlineError", "hyperlink", "appearance", "floatLabel", "model"], outputs: ["modelChange", "sdChange", "sdFocus", "sdBlur", "keyupEnter", "cleared", "sdFocusForceBlur"] }, { kind: "component", type: SdDate, selector: "sd-date", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "required", "disabled", "viewed", "clearable", "inlineError", "hyperlink", "appearance", "floatLabel", "min", "minDate", "max", "maxDate", "transform", "model"], outputs: ["modelChange", "sdChange", "sdFocus"] }, { kind: "component", type: SdDatetime, selector: "sd-datetime", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "required", "disabled", "viewed", "showSeconds", "clearable", "inlineError", "hyperlink", "appearance", "floatLabel", "min", "minDate", "max", "maxDate", "transform", "model"], outputs: ["modelChange", "sdChange", "sdFocus"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
695
763
  }
696
764
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdQueryChipPopover, decorators: [{
697
765
  type: Component,
@@ -708,7 +776,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
708
776
  SdInputNumber,
709
777
  SdDate,
710
778
  SdDatetime,
711
- ], template: "<!--\n Chip popover \u2014 operator + value editor for the popover-mode chip.\n Visual layout mirrors the previous inline `<mat-menu #chipPopover>` in query-bar.\n Body branches on operator + field.type \u2014 8 paths preserved exactly.\n-->\n<mat-menu #menu=\"matMenu\" class=\"c-chip-popover\" xPosition=\"after\" yPosition=\"below\" (closed)=\"onMenuClosed()\">\n <ng-template matMenuContent>\n @let _field = field();\n @let _editingOperator = editingOperator();\n @let _editingValue = editingValue();\n @let _editingOptions = editingOptions();\n @let _editingOptionsLoading = editingOptionsLoading();\n @let _switchPickerMenu = switchPickerMenu();\n\n @if (_field) {\n <div class=\"c-pop-header\" (click)=\"$event.stopPropagation()\">\n <sd-icon [name]=\"iconFor(_field)\"></sd-icon>\n <!-- why: nested mat-menu trigger to swap the chip's field \u2014 parent provides\n the field-picker MatMenu via [switchPickerMenu]. -->\n @if (_switchPickerMenu) {\n <button\n type=\"button\"\n class=\"c-pop-header-field\"\n [matMenuTriggerFor]=\"_switchPickerMenu\"\n matTooltip=\"\u0110\u1ED5i field\">\n <span>{{ _field.label }}</span>\n <sd-icon name=\"unfold_more\"></sd-icon>\n </button>\n } @else {\n <span class=\"c-pop-header-field\">{{ _field.label }}</span>\n }\n <!-- Operator selector (right-aligned). Disabled when field has \u22641 operator. -->\n <sd-operator\n class=\"c-pop-header-operator\"\n [autoId]=\"chipAutoId('operator')\"\n [operators]=\"allowedOperators()\"\n [model]=\"_editingOperator\"\n [disabled]=\"!showOperatorSelector()\"\n (modelChange)=\"onEditingOperatorChange($any($event))\" />\n </div>\n\n <div class=\"c-pop-body\" (click)=\"$event.stopPropagation()\">\n @if (!isNoDataOperator(_editingOperator)) {\n @if (_editingOperator === 'BETWEEN') {\n @if (_field.type === 'date') {\n <div class=\"c-pop-between\">\n <sd-date size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-from')\"\n placeholder=\"T\u1EEB\"\n [model]=\"$any(_editingValue)?.from\"\n (sdChange)=\"onEditingRangeFrom($event)\">\n </sd-date>\n <sd-date size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-to')\"\n placeholder=\"\u0110\u1EBFn\"\n [model]=\"$any(_editingValue)?.to\"\n (sdChange)=\"onEditingRangeTo($event)\">\n </sd-date>\n </div>\n } @else if (_field.type === 'datetime') {\n <div class=\"c-pop-between\">\n <sd-datetime size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-from')\"\n placeholder=\"T\u1EEB\"\n [model]=\"$any(_editingValue)?.from\"\n (sdChange)=\"onEditingRangeFrom($event)\">\n </sd-datetime>\n <sd-datetime size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-to')\"\n placeholder=\"\u0110\u1EBFn\"\n [model]=\"$any(_editingValue)?.to\"\n (sdChange)=\"onEditingRangeTo($event)\">\n </sd-datetime>\n </div>\n } @else {\n <div class=\"c-pop-between\">\n <sd-input-number size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-from')\"\n placeholder=\"T\u1EEB\"\n [model]=\"$any(_editingValue)?.from\"\n (sdChange)=\"onEditingRangeFrom($event)\">\n </sd-input-number>\n <sd-input-number size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-to')\"\n placeholder=\"\u0110\u1EBFn\"\n [model]=\"$any(_editingValue)?.to\"\n (sdChange)=\"onEditingRangeTo($event)\">\n </sd-input-number>\n </div>\n }\n } @else if (_field.type === 'boolean') {\n <div class=\"c-pop-boolean c-bool-toggle\">\n <sd-button\n [autoId]=\"chipAutoId('value-true')\"\n [type]=\"_editingValue === true ? 'fill' : 'outline'\"\n color=\"success\"\n [title]=\"$any(_field).trueLabel || 'C\u00F3'\"\n (click)=\"onEditingValueInput(true)\">\n </sd-button>\n <sd-button\n [autoId]=\"chipAutoId('value-false')\"\n [type]=\"_editingValue === false ? 'fill' : 'outline'\"\n color=\"warning\"\n [title]=\"$any(_field).falseLabel || 'Kh\u00F4ng'\"\n (click)=\"onEditingValueInput(false)\">\n </sd-button>\n </div>\n } @else if (_field.type === 'values' || _field.type === 'lazy-values') {\n @let _opt = $any(_field).option;\n <sd-select size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value')\"\n placeholder=\"Ch\u1ECDn gi\u00E1 tr\u1ECB\"\n [items]=\"_editingOptions\"\n [valueField]=\"_opt.valueField\"\n [displayField]=\"_opt.displayField\"\n [multiple]=\"multiple()\"\n [model]=\"$any(_editingValue)\"\n (sdChange)=\"onEditingValueInput($event)\">\n </sd-select>\n @if (_editingOptionsLoading) {\n <div class=\"c-pop-values-loading\">\u0110ang t\u1EA3i...</div>\n }\n } @else if (_field.type === 'date') {\n <sd-date size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value')\"\n placeholder=\"Ch\u1ECDn gi\u00E1 tr\u1ECB\"\n [model]=\"$any(_editingValue)\"\n (sdChange)=\"onEditingValueInput($event)\">\n </sd-date>\n } @else if (_field.type === 'datetime') {\n <sd-datetime size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value')\"\n placeholder=\"Ch\u1ECDn gi\u00E1 tr\u1ECB\"\n [model]=\"$any(_editingValue)\"\n (sdChange)=\"onEditingValueInput($event)\">\n </sd-datetime>\n } @else if (_field.type === 'number') {\n <sd-input-number size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value')\"\n placeholder=\"Nh\u1EADp gi\u00E1 tr\u1ECB\"\n [model]=\"$any(_editingValue)\"\n (sdChange)=\"onEditingValueInput($event)\">\n </sd-input-number>\n } @else {\n <sd-input size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value')\"\n placeholder=\"Nh\u1EADp gi\u00E1 tr\u1ECB\"\n [model]=\"$any(_editingValue)\"\n (sdChange)=\"onEditingValueInput($event)\">\n </sd-input>\n }\n }\n </div>\n }\n </ng-template>\n</mat-menu>\n", styles: [":host{display:contents}::ng-deep .mat-mdc-menu-panel.c-chip-popover{min-width:280px;max-width:320px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header{display:flex;align-items:center;gap:8px;padding:10px 12px;border-bottom:1px solid #e2e4e7;font-size:13px;font-weight:500;color:#212121}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header sd-icon{font-size:16px;width:16px;height:16px;color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header .c-pop-header-operator{margin-left:auto}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field{display:inline-flex;align-items:center;gap:4px;padding:2px 4px 2px 6px;margin-left:-4px;background:transparent;border:1px solid transparent;border-radius:4px;font:inherit;font-weight:500;color:#212121;cursor:pointer;transition:background-color .12s ease,border-color .12s ease}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field sd-icon{font-size:14px!important;width:14px!important;height:14px!important;color:#7a7a7a!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field:hover{background:#f5f6f7;border-color:#e2e4e7}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-body{padding:10px 12px;display:flex;flex-direction:column;gap:12px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-between{display:grid;grid-template-columns:1fr 1fr;gap:8px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-boolean{display:flex;gap:8px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-boolean button{flex:1;height:32px;border:1px solid #d6d8db;border-radius:6px;background:#fff;font:inherit;font-size:13px;cursor:pointer}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values-loading{padding:10px 8px;font-size:12px;color:#7a7a7a}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-bool-toggle{display:inline-flex;align-items:center;gap:4px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-bool-toggle .mdc-button,::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-bool-toggle button[mat-button],::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-bool-toggle button[mat-stroked-button],::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-bool-toggle button[mat-flat-button]{border-radius:999px;min-width:0;padding:0 12px}\n"] }]
779
+ ], template: "<!--\n Chip popover \u2014 operator + value editor for the popover-mode chip.\n Visual layout mirrors the previous inline `<mat-menu #chipPopover>` in query-bar.\n Body branches on operator + field.type \u2014 8 paths preserved exactly.\n-->\n<mat-menu #menu=\"matMenu\" class=\"c-chip-popover\" xPosition=\"after\" yPosition=\"below\" (closed)=\"onMenuClosed()\">\n <ng-template matMenuContent>\n @let _field = field();\n @let _editingOperator = editingOperator();\n @let _editingValue = editingValue();\n @let _editingOptions = editingOptions();\n @let _editingOptionsLoading = editingOptionsLoading();\n @let _switchPickerMenu = switchPickerMenu();\n\n @if (_field) {\n <!-- why: header/body c\u1EE7a popover l\u00E0 NH\u00D3M control (role=\"group\"), kh\u00F4ng ph\u1EA3i n\u00FAt \u2014 n\u00EAn ch\u00FAng\n KH\u00D4NG \u0111\u01B0\u1EE3c nh\u1EADn tabindex; tab ph\u1EA3i \u0111i th\u1EB3ng v\u00E0o c\u00E1c control b\u00EAn trong. Th\u00EAm ch\u1EB7n Enter\n b\u00EAn c\u1EA1nh ch\u1EB7n click: c\u1EA3 hai ch\u1EC9 \u0111\u1EC3 mat-menu kh\u00F4ng t\u1EF1 \u0111\u00F3ng khi ng\u01B0\u1EDDi d\u00F9ng thao t\u00E1c trong\n popover; Escape v\u00E0 ph\u00EDm m\u0169i t\u00EAn v\u1EABn b\u1ECDt l\u00EAn \u0111\u1EC3 menu \u0111\u00F3ng/\u0111i\u1EC1u h\u01B0\u1EDBng b\u00ECnh th\u01B0\u1EDDng. -->\n <div class=\"c-pop-header\" role=\"group\" (click)=\"$event.stopPropagation()\" (keydown.enter)=\"$event.stopPropagation()\">\n <sd-icon [name]=\"iconFor(_field)\"></sd-icon>\n <!-- why: nested mat-menu trigger to swap the chip's field \u2014 parent provides\n the field-picker MatMenu via [switchPickerMenu]. -->\n @if (_switchPickerMenu) {\n <button\n type=\"button\"\n class=\"c-pop-header-field\"\n [matMenuTriggerFor]=\"_switchPickerMenu\"\n [matTooltip]=\"swapFieldTooltip()\">\n <span>{{ _field.label }}</span>\n <sd-icon name=\"unfold_more\"></sd-icon>\n </button>\n } @else {\n <span class=\"c-pop-header-field\">{{ _field.label }}</span>\n }\n <!-- Operator selector (right-aligned). Disabled when field has \u22641 operator. -->\n <sd-operator\n class=\"c-pop-header-operator\"\n [autoId]=\"chipAutoId('operator')\"\n [operators]=\"allowedOperators()\"\n [model]=\"_editingOperator\"\n [disabled]=\"!showOperatorSelector()\"\n (modelChange)=\"onEditingOperatorChange($any($event))\" />\n </div>\n\n <div class=\"c-pop-body\" role=\"group\" (click)=\"$event.stopPropagation()\" (keydown.enter)=\"$event.stopPropagation()\">\n @if (!isNoDataOperator(_editingOperator)) {\n @if (_editingOperator === 'BETWEEN') {\n @if (_field.type === 'date') {\n <div class=\"c-pop-between\">\n <sd-date size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-from')\"\n [placeholder]=\"fromPlaceholder()\"\n [model]=\"$any(_editingValue)?.from\"\n (sdChange)=\"onEditingRangeFrom($event)\">\n </sd-date>\n <sd-date size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-to')\"\n [placeholder]=\"toPlaceholder()\"\n [model]=\"$any(_editingValue)?.to\"\n (sdChange)=\"onEditingRangeTo($event)\">\n </sd-date>\n </div>\n } @else if (_field.type === 'datetime') {\n <div class=\"c-pop-between\">\n <sd-datetime size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-from')\"\n [placeholder]=\"fromPlaceholder()\"\n [model]=\"$any(_editingValue)?.from\"\n (sdChange)=\"onEditingRangeFrom($event)\">\n </sd-datetime>\n <sd-datetime size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-to')\"\n [placeholder]=\"toPlaceholder()\"\n [model]=\"$any(_editingValue)?.to\"\n (sdChange)=\"onEditingRangeTo($event)\">\n </sd-datetime>\n </div>\n } @else {\n <div class=\"c-pop-between\">\n <sd-input-number size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-from')\"\n [placeholder]=\"fromPlaceholder()\"\n [model]=\"$any(_editingValue)?.from\"\n (sdChange)=\"onEditingRangeFrom($event)\">\n </sd-input-number>\n <sd-input-number size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value-to')\"\n [placeholder]=\"toPlaceholder()\"\n [model]=\"$any(_editingValue)?.to\"\n (sdChange)=\"onEditingRangeTo($event)\">\n </sd-input-number>\n </div>\n }\n } @else if (_field.type === 'boolean') {\n <div class=\"c-pop-boolean c-bool-toggle\">\n <sd-button\n [autoId]=\"chipAutoId('value-true')\"\n [type]=\"_editingValue === true ? 'fill' : 'outline'\"\n color=\"success\"\n [title]=\"$any(_field).trueLabel || booleanTrueLabel()\"\n (click)=\"onEditingValueInput(true)\">\n </sd-button>\n <sd-button\n [autoId]=\"chipAutoId('value-false')\"\n [type]=\"_editingValue === false ? 'fill' : 'outline'\"\n color=\"warning\"\n [title]=\"$any(_field).falseLabel || booleanFalseLabel()\"\n (click)=\"onEditingValueInput(false)\">\n </sd-button>\n </div>\n } @else if (_field.type === 'values' || _field.type === 'lazy-values') {\n @let _opt = $any(_field).option;\n <sd-select size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value')\"\n [placeholder]=\"selectValuePlaceholder()\"\n [items]=\"_editingOptions\"\n [valueField]=\"_opt.valueField\"\n [displayField]=\"_opt.displayField\"\n [multiple]=\"multiple()\"\n [model]=\"$any(_editingValue)\"\n (sdChange)=\"onEditingValueInput($event)\">\n </sd-select>\n @if (_editingOptionsLoading) {\n <div class=\"c-pop-values-loading\">{{ loadingValuesLabel() }}</div>\n }\n } @else if (_field.type === 'date') {\n <sd-date size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value')\"\n [placeholder]=\"selectValuePlaceholder()\"\n [model]=\"$any(_editingValue)\"\n (sdChange)=\"onEditingValueInput($event)\">\n </sd-date>\n } @else if (_field.type === 'datetime') {\n <sd-datetime size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value')\"\n [placeholder]=\"selectValuePlaceholder()\"\n [model]=\"$any(_editingValue)\"\n (sdChange)=\"onEditingValueInput($event)\">\n </sd-datetime>\n } @else if (_field.type === 'number') {\n <sd-input-number size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value')\"\n [placeholder]=\"enterValuePlaceholder()\"\n [model]=\"$any(_editingValue)\"\n (sdChange)=\"onEditingValueInput($event)\">\n </sd-input-number>\n } @else {\n <sd-input size=\"sm\" hideInlineError\n [autoId]=\"chipAutoId('value')\"\n [placeholder]=\"enterValuePlaceholder()\"\n [model]=\"$any(_editingValue)\"\n (sdChange)=\"onEditingValueInput($event)\">\n </sd-input>\n }\n }\n </div>\n }\n </ng-template>\n</mat-menu>\n", styles: [":host{display:contents}::ng-deep .mat-mdc-menu-panel.c-chip-popover{min-width:280px;max-width:320px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header{display:flex;align-items:center;gap:8px;padding:10px 12px;border-bottom:1px solid #e2e4e7;font-size:13px;font-weight:500;color:#212121}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header sd-icon{font-size:16px;width:16px;height:16px;color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header .c-pop-header-operator{margin-left:auto}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field{display:inline-flex;align-items:center;gap:4px;padding:2px 4px 2px 6px;margin-left:-4px;background:transparent;border:1px solid transparent;border-radius:4px;font:inherit;font-weight:500;color:#212121;cursor:pointer;transition:background-color .12s ease,border-color .12s ease}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field sd-icon{font-size:14px!important;width:14px!important;height:14px!important;color:#7a7a7a!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field:hover{background:#f5f6f7;border-color:#e2e4e7}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-body{padding:10px 12px;display:flex;flex-direction:column;gap:12px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-between{display:grid;grid-template-columns:1fr 1fr;gap:8px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-boolean{display:flex;gap:8px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-boolean button{flex:1;height:32px;border:1px solid #d6d8db;border-radius:6px;background:#fff;font:inherit;font-size:13px;cursor:pointer}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values-loading{padding:10px 8px;font-size:12px;color:#7a7a7a}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-bool-toggle{display:inline-flex;align-items:center;gap:4px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-bool-toggle .mdc-button,::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-bool-toggle button[mat-button],::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-bool-toggle button[mat-stroked-button],::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-bool-toggle button[mat-flat-button]{border-radius:999px;min-width:0;padding:0 12px}\n"] }]
712
780
  }], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: false }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: false }] }], chipIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "chipIndex", required: false }] }], autoIdBase: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoIdBase", required: false }] }], switchPickerMenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "switchPickerMenu", required: false }] }], commit: [{ type: i0.Output, args: ["commit"] }], swapField: [{ type: i0.Output, args: ["swapField"] }], menu: [{ type: i0.ViewChild, args: ['menu', { isSignal: true }] }] } });
713
781
 
714
782
  /**
@@ -724,6 +792,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
724
792
  * chosen field through `(pick)`.
725
793
  */
726
794
  class SdQueryFieldPicker {
795
+ #i18n = inject(I18nService);
796
+ emptyLabel = computed(() => this.#i18n.t('core.component.query-bar.field-empty'), ...(ngDevMode ? [{ debugName: "emptyLabel" }] : /* istanbul ignore next */ []));
727
797
  /** Field list shown in the menu (caller-supplied; rendered in input order). */
728
798
  fields = input([], ...(ngDevMode ? [{ debugName: "fields" }] : /* istanbul ignore next */ []));
729
799
  /** Single key disabled with a trailing check — used in the chip-popover "Đổi field" flow. */
@@ -755,11 +825,11 @@ class SdQueryFieldPicker {
755
825
  return this.usedKeys().has(field.key);
756
826
  }
757
827
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdQueryFieldPicker, deps: [], target: i0.ɵɵFactoryTarget.Component });
758
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SdQueryFieldPicker, isStandalone: true, selector: "sd-query-field-picker", inputs: { fields: { classPropertyName: "fields", publicName: "fields", isSignal: true, isRequired: false, transformFunction: null }, currentKey: { classPropertyName: "currentKey", publicName: "currentKey", isSignal: true, isRequired: false, transformFunction: null }, usedKeys: { classPropertyName: "usedKeys", publicName: "usedKeys", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pick: "pick" }, viewQueries: [{ propertyName: "menu", first: true, predicate: MatMenu, descendants: true, isSignal: true }], ngImport: i0, template: "<mat-menu #m=\"matMenu\" xPosition=\"after\" yPosition=\"below\" class=\"c-field-picker-menu\">\n @for (field of fields(); track $index) {\n <button\n type=\"button\"\n mat-menu-item\n [disabled]=\"isDisabled(field)\"\n (click)=\"pick.emit(field)\">\n <sd-icon class=\"c-field-item-icon\" [name]=\"iconFor(field)\"></sd-icon>\n <span class=\"c-field-item-label\">{{ field.label }}</span>\n <span class=\"c-field-item-type\">{{ field.type }}</span>\n @if (isCurrent(field) || isUsed(field)) {\n <sd-icon class=\"c-field-item-check\" name=\"check\"></sd-icon>\n }\n </button>\n } @empty {\n <div class=\"c-field-empty\">Ch\u01B0a c\u00F3 field n\u00E0o \u0111\u01B0\u1EE3c c\u1EA5u h\u00ECnh.</div>\n }\n</mat-menu>\n", styles: ["::ng-deep .mat-mdc-menu-panel.c-field-picker-menu{min-width:200px;max-width:260px}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-menu-item{min-height:36px}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-menu-item .mat-mdc-menu-item-text{display:flex;align-items:center;flex:1;min-width:0}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .c-field-item-icon{font-size:16px;width:16px;height:16px;margin-right:8px;color:#5f6368;flex-shrink:0}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .c-field-item-label{flex:1 1 auto;min-width:0;font-size:13px;color:#212121;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .c-field-item-type{flex-shrink:0;margin-left:auto;padding-left:12px;font-family:Roboto Mono,monospace;font-size:11px;color:#7a7a7a}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .c-field-item-check{flex-shrink:0;font-size:14px;width:14px;height:14px;margin-left:6px;color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .c-field-empty{padding:12px 16px;font-size:13px;color:#7a7a7a}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i1.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
828
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SdQueryFieldPicker, isStandalone: true, selector: "sd-query-field-picker", inputs: { fields: { classPropertyName: "fields", publicName: "fields", isSignal: true, isRequired: false, transformFunction: null }, currentKey: { classPropertyName: "currentKey", publicName: "currentKey", isSignal: true, isRequired: false, transformFunction: null }, usedKeys: { classPropertyName: "usedKeys", publicName: "usedKeys", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pick: "pick" }, viewQueries: [{ propertyName: "menu", first: true, predicate: MatMenu, descendants: true, isSignal: true }], ngImport: i0, template: "<mat-menu #m=\"matMenu\" xPosition=\"after\" yPosition=\"below\" class=\"c-field-picker-menu\">\n @for (field of fields(); track $index) {\n <button\n type=\"button\"\n mat-menu-item\n [disabled]=\"isDisabled(field)\"\n (click)=\"pick.emit(field)\">\n <sd-icon class=\"c-field-item-icon\" [name]=\"iconFor(field)\"></sd-icon>\n <span class=\"c-field-item-label\">{{ field.label }}</span>\n <span class=\"c-field-item-type\">{{ field.type }}</span>\n @if (isCurrent(field) || isUsed(field)) {\n <sd-icon class=\"c-field-item-check\" name=\"check\"></sd-icon>\n }\n </button>\n } @empty {\n <div class=\"c-field-empty\">{{ emptyLabel() }}</div>\n }\n</mat-menu>\n", styles: ["::ng-deep .mat-mdc-menu-panel.c-field-picker-menu{min-width:200px;max-width:260px}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-menu-item{min-height:36px}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-menu-item .mat-mdc-menu-item-text{display:flex;align-items:center;flex:1;min-width:0}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .c-field-item-icon{font-size:16px;width:16px;height:16px;margin-right:8px;color:#5f6368;flex-shrink:0}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .c-field-item-label{flex:1 1 auto;min-width:0;font-size:13px;color:#212121;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .c-field-item-type{flex-shrink:0;margin-left:auto;padding-left:12px;font-family:Roboto Mono,monospace;font-size:11px;color:#7a7a7a}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .c-field-item-check{flex-shrink:0;font-size:14px;width:14px;height:14px;margin-left:6px;color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .c-field-empty{padding:12px 16px;font-size:13px;color:#7a7a7a}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i1.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
759
829
  }
760
830
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdQueryFieldPicker, decorators: [{
761
831
  type: Component,
762
- args: [{ selector: 'sd-query-field-picker', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [SdIcon, MatMenuModule], template: "<mat-menu #m=\"matMenu\" xPosition=\"after\" yPosition=\"below\" class=\"c-field-picker-menu\">\n @for (field of fields(); track $index) {\n <button\n type=\"button\"\n mat-menu-item\n [disabled]=\"isDisabled(field)\"\n (click)=\"pick.emit(field)\">\n <sd-icon class=\"c-field-item-icon\" [name]=\"iconFor(field)\"></sd-icon>\n <span class=\"c-field-item-label\">{{ field.label }}</span>\n <span class=\"c-field-item-type\">{{ field.type }}</span>\n @if (isCurrent(field) || isUsed(field)) {\n <sd-icon class=\"c-field-item-check\" name=\"check\"></sd-icon>\n }\n </button>\n } @empty {\n <div class=\"c-field-empty\">Ch\u01B0a c\u00F3 field n\u00E0o \u0111\u01B0\u1EE3c c\u1EA5u h\u00ECnh.</div>\n }\n</mat-menu>\n", styles: ["::ng-deep .mat-mdc-menu-panel.c-field-picker-menu{min-width:200px;max-width:260px}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-menu-item{min-height:36px}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-menu-item .mat-mdc-menu-item-text{display:flex;align-items:center;flex:1;min-width:0}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .c-field-item-icon{font-size:16px;width:16px;height:16px;margin-right:8px;color:#5f6368;flex-shrink:0}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .c-field-item-label{flex:1 1 auto;min-width:0;font-size:13px;color:#212121;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .c-field-item-type{flex-shrink:0;margin-left:auto;padding-left:12px;font-family:Roboto Mono,monospace;font-size:11px;color:#7a7a7a}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .c-field-item-check{flex-shrink:0;font-size:14px;width:14px;height:14px;margin-left:6px;color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .c-field-empty{padding:12px 16px;font-size:13px;color:#7a7a7a}\n"] }]
832
+ args: [{ selector: 'sd-query-field-picker', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [SdIcon, MatMenuModule], template: "<mat-menu #m=\"matMenu\" xPosition=\"after\" yPosition=\"below\" class=\"c-field-picker-menu\">\n @for (field of fields(); track $index) {\n <button\n type=\"button\"\n mat-menu-item\n [disabled]=\"isDisabled(field)\"\n (click)=\"pick.emit(field)\">\n <sd-icon class=\"c-field-item-icon\" [name]=\"iconFor(field)\"></sd-icon>\n <span class=\"c-field-item-label\">{{ field.label }}</span>\n <span class=\"c-field-item-type\">{{ field.type }}</span>\n @if (isCurrent(field) || isUsed(field)) {\n <sd-icon class=\"c-field-item-check\" name=\"check\"></sd-icon>\n }\n </button>\n } @empty {\n <div class=\"c-field-empty\">{{ emptyLabel() }}</div>\n }\n</mat-menu>\n", styles: ["::ng-deep .mat-mdc-menu-panel.c-field-picker-menu{min-width:200px;max-width:260px}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-menu-item{min-height:36px}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-menu-item .mat-mdc-menu-item-text{display:flex;align-items:center;flex:1;min-width:0}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .c-field-item-icon{font-size:16px;width:16px;height:16px;margin-right:8px;color:#5f6368;flex-shrink:0}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .c-field-item-label{flex:1 1 auto;min-width:0;font-size:13px;color:#212121;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .c-field-item-type{flex-shrink:0;margin-left:auto;padding-left:12px;font-family:Roboto Mono,monospace;font-size:11px;color:#7a7a7a}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .c-field-item-check{flex-shrink:0;font-size:14px;width:14px;height:14px;margin-left:6px;color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .c-field-empty{padding:12px 16px;font-size:13px;color:#7a7a7a}\n"] }]
763
833
  }], propDecorators: { fields: [{ type: i0.Input, args: [{ isSignal: true, alias: "fields", required: false }] }], currentKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentKey", required: false }] }], usedKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "usedKeys", required: false }] }], pick: [{ type: i0.Output, args: ["pick"] }], menu: [{ type: i0.ViewChild, args: [i0.forwardRef(() => MatMenu), { isSignal: true }] }] } });
764
834
 
765
835
  /**
@@ -779,6 +849,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
779
849
  * BETWEEN) vẫn dùng lifecycle thủ công cho tới khi control tương ứng có inline mode (rollout).
780
850
  */
781
851
  class SdQueryInlineChip {
852
+ #i18n = inject(I18nService);
853
+ // why: nhãn mặc định của field boolean phải trùng với nhãn parent dựng cho chip (`chipValueText`)
854
+ // và với build chip, nếu không cùng một field sẽ hiện hai ngôn ngữ tuỳ chỗ.
855
+ booleanTrueLabel = computed(() => this.#i18n.t('core.component.query-bar.boolean.true'), ...(ngDevMode ? [{ debugName: "booleanTrueLabel" }] : /* istanbul ignore next */ []));
856
+ booleanFalseLabel = computed(() => this.#i18n.t('core.component.query-bar.boolean.false'), ...(ngDevMode ? [{ debugName: "booleanFalseLabel" }] : /* istanbul ignore next */ []));
782
857
  // ---------------------------------------------------------------------------
783
858
  // Inputs
784
859
  // ---------------------------------------------------------------------------
@@ -861,11 +936,11 @@ class SdQueryInlineChip {
861
936
  this.#editing.set(false);
862
937
  }
863
938
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdQueryInlineChip, deps: [], target: i0.ɵɵFactoryTarget.Component });
864
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SdQueryInlineChip, isStandalone: true, selector: "sd-query-inline-chip", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: true, transformFunction: null }, density: { classPropertyName: "density", publicName: "density", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, showOperator: { classPropertyName: "showOperator", publicName: "showOperator", isSignal: true, isRequired: false, transformFunction: null }, autoId: { classPropertyName: "autoId", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, valueText: { classPropertyName: "valueText", publicName: "valueText", isSignal: true, isRequired: false, transformFunction: null }, isNoData: { classPropertyName: "isNoData", publicName: "isNoData", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { commit: "commit", commitRange: "commitRange", liveChange: "liveChange", remove: "remove" }, ngImport: i0, template: "<!--\n Completed inline chip \u2014 non-string/number/seamless branches.\n Edit lifecycle is self-managed: click enters edit + auto-opens picker,\n focusout outside the wrapper exits edit, picker emits commit single OR\n liveChange (multi) OR commitRange (BETWEEN).\n-->\n@let _field = field();\n@let _operator = operator();\n@let _data = data();\n@let _multiple = multiple();\n@let _editing = editing();\n@let _isNoData = isNoData();\n@let _autoId = autoId();\n\n<div class=\"c-token\" [class.c-token-editing]=\"_editing\">\n <sd-icon class=\"c-token-icon\" [name]=\"iconName()\"></sd-icon>\n <span class=\"c-token-field\">{{ _field.label }}</span>\n\n @if (!_isNoData && showOperator()) {\n <sd-operator class=\"c-token-op\" [operators]=\"[_operator]\" [model]=\"_operator\" [disabled]=\"true\" />\n } @else if (!_isNoData) {\n <span class=\"c-token-sep\">:</span>\n }\n\n @if (!_isNoData) {\n @if (_field.type === 'boolean') {\n @if (_editing) {\n <!-- why: native button thay sd-button \u2014 mat-button MDC baseline 36px bu\u1ED9c\n ph\u1EA3i hack height/padding overrides; chip 28/32px nh\u1ECF h\u01A1n nhi\u1EC1u. T\u1EF1 v\u1EBD\n pill nh\u1ECF v\u1EDBi CSS scoped trong chip, kh\u00F4ng qua MDC, g\u1ECDn + chu\u1EA9n baseline. -->\n <span class=\"c-token-value c-token-value-edit c-bool-toggle\">\n <button\n type=\"button\"\n class=\"c-bool-btn c-bool-true\"\n [class.c-bool-active]=\"_data === true\"\n [attr.data-autoId]=\"_autoId + '-true'\"\n (click)=\"emitBoolean(true)\">\n {{ $any(_field).trueLabel || 'C\u00F3' }}\n </button>\n <button\n type=\"button\"\n class=\"c-bool-btn c-bool-false\"\n [class.c-bool-active]=\"_data === false\"\n [attr.data-autoId]=\"_autoId + '-false'\"\n (click)=\"emitBoolean(false)\">\n {{ $any(_field).falseLabel || 'Kh\u00F4ng' }}\n </button>\n </span>\n } @else {\n <button type=\"button\" class=\"c-token-value\" (click)=\"enterEdit()\">{{ valueText() }}</button>\n }\n } @else if ((_field.type === 'date' || _field.type === 'datetime') && _operator === 'BETWEEN') {\n <!-- why: sd-date-range [viewed]=\"'inline'\" t\u1EF1 qu\u1EA3n click\u2192edit + focusout (datetime downgrade v\u1EC1 date).\n Chip kh\u00F4ng wiring lifecycle n\u1EEFa; [clearable]=false v\u00EC chip c\u00F3 \u00D7 ri\u00EAng. -->\n <span class=\"c-token-value c-token-value-edit\">\n <sd-date-range size=\"sm\"\n [autoId]=\"_autoId\"\n [viewed]=\"'inline'\"\n [clearable]=\"false\"\n [model]=\"$any(_data)\"\n (sdChange)=\"emitRangeCommit($any($event))\"></sd-date-range>\n </span>\n } @else if (_field.type === 'values' || _field.type === 'lazy-values') {\n @let _opt = $any(_field).option;\n <!-- why: sd-select [viewed]=\"'inline'\" t\u1EF1 qu\u1EA3n click\u2192edit + focusout\u2192tho\u00E1t + bare editor.\n Chip kh\u00F4ng wiring lifecycle cho nh\u00E1nh n\u00E0y n\u1EEFa (b\u1ECF enterEdit/onFocusOut/bare). -->\n <span class=\"c-token-value c-token-value-edit\">\n <sd-select #chipPicker size=\"sm\" minWidthPanel=\"300px\"\n [autoId]=\"_autoId\"\n [items]=\"_field.type === 'lazy-values' ? $any(_opt).search : $any(_opt.items)\"\n [valueField]=\"_opt.valueField\"\n [displayField]=\"_opt.displayField\"\n [multiple]=\"_multiple\"\n [viewed]=\"'inline'\"\n [clearable]=\"false\"\n [model]=\"$any(_data)\"\n (sdChange)=\"_multiple ? emitLive($event) : emitSingleCommit($event)\">\n <!-- why: viewed mode m\u1EB7c \u0111\u1ECBnh d\u00F9ng comma-joined cho multi \u2014 qu\u00E1 d\u00E0i cho chip.\n Override b\u1EB1ng template \"head +N\" gi\u1ED1ng popover chipValueText. -->\n <ng-template #sdValue let-selectedItems=\"selectedItems\">\n @if ((selectedItems?.length ?? 0) > 1) {\n <span class=\"c-token-multi-head\">{{ $any(selectedItems)[0][_opt.displayField] }}</span> +{{ selectedItems.length - 1 }}\n } @else if ((selectedItems?.length ?? 0) === 1) {\n <span class=\"c-token-multi-head\">{{ $any(selectedItems)[0][_opt.displayField] }}</span>\n }\n </ng-template>\n </sd-select>\n </span>\n } @else if (_field.type === 'date') {\n <span class=\"c-token-value c-token-value-edit\">\n <sd-date size=\"sm\"\n [autoId]=\"_autoId\"\n [viewed]=\"'inline'\"\n [clearable]=\"false\"\n [model]=\"$any(_data)\"\n (sdChange)=\"emitSingleCommit($any($event))\"></sd-date>\n </span>\n } @else if (_field.type === 'datetime') {\n <span class=\"c-token-value c-token-value-edit\">\n <sd-datetime size=\"sm\"\n [autoId]=\"_autoId\"\n [viewed]=\"'inline'\"\n [clearable]=\"false\"\n [model]=\"$any(_data)\"\n (sdChange)=\"emitSingleCommit($any($event))\"></sd-datetime>\n </span>\n } @else {\n <!-- Fallback (unforeseen type) \u2014 read-only display via valueText. -->\n <span class=\"c-token-value\">{{ valueText() }}</span>\n }\n }\n\n <button type=\"button\" class=\"c-token-remove\" aria-label=\"Remove filter\" (click)=\"remove.emit()\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n</div>\n", styles: [":host{display:contents}.c-token{display:inline-flex;align-items:center;gap:4px;padding:0 0 0 12px;border:1px solid var(--sd-primary-light);border-radius:999px;background:var(--sd-primary-light);font-size:13px;color:#5f6368;white-space:nowrap;overflow:hidden}:host-context(.c-density-compact) .c-token{height:28px}:host-context(.c-density-comfortable) .c-token{height:32px}.c-token-icon{font-size:14px;width:14px;height:14px;line-height:14px;color:inherit}.c-token-field{font-weight:500;line-height:1}.c-token-op{display:inline-flex;align-items:center;color:inherit}.c-token-op ::ng-deep .c-op-trigger{padding:0;color:inherit}.c-token-op ::ng-deep .c-op-icon svg{width:14px;height:14px;display:block}.c-token-sep{color:#7a7a7a}.c-token-value{border:none;background:transparent;padding:0;font:inherit;font-weight:500;color:var(--sd-primary);cursor:pointer}.c-token-value:hover{text-decoration:underline}.c-token-value-edit{display:inline-flex;align-items:center;gap:4px;max-width:240px}.c-token-value-edit sd-date-range{display:inline-flex;max-width:220px;min-width:0;flex:0 1 auto}.c-token-value-edit sd-select,.c-token-value-edit sd-date,.c-token-value-edit sd-datetime{display:inline-flex;align-items:center;line-height:1}.c-token ::ng-deep .mat-mdc-form-field{line-height:1}.c-token ::ng-deep .mat-mdc-text-field-wrapper,.c-token ::ng-deep .mat-mdc-form-field-flex{padding:0;min-height:0;background:transparent}.c-token ::ng-deep .mat-mdc-form-field-infix{padding:0;min-height:0;width:auto;border:0;display:inline-flex;align-items:center}.c-token ::ng-deep .mat-mdc-select-value,.c-token ::ng-deep .mat-mdc-input-element,.c-token ::ng-deep input{line-height:1;font-size:13px;padding:0;height:auto;color:inherit}.c-token ::ng-deep .mat-mdc-select-value{display:inline-flex;align-items:center;min-width:0;max-width:200px;overflow:hidden;text-overflow:ellipsis;color:var(--sd-primary);font-weight:500}.c-token ::ng-deep .sd-suffix-icon{display:none}.c-token-multi-head{display:inline-block;max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom}.c-bool-toggle{display:inline-flex;align-items:center;gap:4px}.c-bool-btn{display:inline-flex;align-items:center;justify-content:center;height:18px;min-width:0;padding:0 8px;border:1px solid currentColor;border-radius:999px;background:transparent;font:inherit;font-size:11px;font-weight:500;line-height:1;cursor:pointer;white-space:nowrap;-webkit-user-select:none;user-select:none;transition:background .12s ease,color .12s ease}:host-context(.c-density-comfortable) .c-bool-btn{height:20px;padding:0 10px;font-size:12px}.c-bool-true{color:var(--sd-success)}.c-bool-false{color:var(--sd-warning)}.c-bool-active.c-bool-true{background:var(--sd-success);color:#fff;border-color:var(--sd-success)}.c-bool-active.c-bool-false{background:var(--sd-warning);color:#fff;border-color:var(--sd-warning)}.c-bool-btn:hover:not(.c-bool-active).c-bool-true{background:var(--sd-success-light)}.c-bool-btn:hover:not(.c-bool-active).c-bool-false{background:var(--sd-warning-light)}.c-token-remove{align-self:stretch;display:inline-flex;align-items:center;justify-content:center;border:none;border-left:1px solid rgba(0,0,0,.04);border-radius:0;background:transparent;cursor:pointer;color:inherit}:host-context(.c-density-compact) .c-token-remove{width:26px}:host-context(.c-density-comfortable) .c-token-remove{width:28px}.c-token-remove sd-icon{font-size:14px;width:14px;height:14px}.c-token-remove:hover{background:#0000000d}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "ngmodule", type: CommonModule }, { kind: "component", type: SdOperator, selector: "sd-operator", inputs: ["model", "operators", "disabled", "autoId"], outputs: ["modelChange"] }, { kind: "component", type: SdDate, selector: "sd-date", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "required", "disabled", "viewed", "clearable", "inlineError", "hyperlink", "appearance", "floatLabel", "min", "minDate", "max", "maxDate", "model"], outputs: ["modelChange", "sdChange", "sdFocus"] }, { kind: "component", type: SdDateRange, selector: "sd-date-range", inputs: ["autoId", "name", "size", "form", "label", "helperText", "hideInlineError", "required", "disabled", "viewed", "clearable", "appearance", "floatLabel", "min", "max", "model"], outputs: ["modelChange", "sdChange"] }, { kind: "component", type: SdDatetime, selector: "sd-datetime", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "required", "disabled", "viewed", "showSeconds", "clearable", "inlineError", "hyperlink", "appearance", "floatLabel", "min", "minDate", "max", "maxDate", "model"], outputs: ["modelChange", "sdChange", "sdFocus"] }, { kind: "component", type: SdSelect, selector: "sd-select", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "valueField", "displayField", "disabledField", "cacheChecksum", "limit", "hyperlink", "minWidthPanel", "hideInlineError", "required", "disabled", "viewed", "multiple", "clearable", "validator", "inlineError", "appearance", "floatLabel", "items", "model"], outputs: ["modelChange", "sdChange", "sdSelection"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
939
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SdQueryInlineChip, isStandalone: true, selector: "sd-query-inline-chip", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: true, transformFunction: null }, density: { classPropertyName: "density", publicName: "density", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, showOperator: { classPropertyName: "showOperator", publicName: "showOperator", isSignal: true, isRequired: false, transformFunction: null }, autoId: { classPropertyName: "autoId", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, valueText: { classPropertyName: "valueText", publicName: "valueText", isSignal: true, isRequired: false, transformFunction: null }, isNoData: { classPropertyName: "isNoData", publicName: "isNoData", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { commit: "commit", commitRange: "commitRange", liveChange: "liveChange", remove: "remove" }, ngImport: i0, template: "<!--\n Completed inline chip \u2014 non-string/number/seamless branches.\n Edit lifecycle is self-managed: click enters edit + auto-opens picker,\n focusout outside the wrapper exits edit, picker emits commit single OR\n liveChange (multi) OR commitRange (BETWEEN).\n-->\n@let _field = field();\n@let _operator = operator();\n@let _data = data();\n@let _multiple = multiple();\n@let _editing = editing();\n@let _isNoData = isNoData();\n@let _autoId = autoId();\n\n<div class=\"c-token\" [class.c-token-editing]=\"_editing\">\n <sd-icon class=\"c-token-icon\" [name]=\"iconName()\"></sd-icon>\n <span class=\"c-token-field\">{{ _field.label }}</span>\n\n @if (!_isNoData && showOperator()) {\n <sd-operator class=\"c-token-op\" [operators]=\"[_operator]\" [model]=\"_operator\" [disabled]=\"true\" />\n } @else if (!_isNoData) {\n <span class=\"c-token-sep\">:</span>\n }\n\n @if (!_isNoData) {\n @if (_field.type === 'boolean') {\n @if (_editing) {\n <!-- why: native button thay sd-button \u2014 mat-button MDC baseline 36px bu\u1ED9c\n ph\u1EA3i hack height/padding overrides; chip 28/32px nh\u1ECF h\u01A1n nhi\u1EC1u. T\u1EF1 v\u1EBD\n pill nh\u1ECF v\u1EDBi CSS scoped trong chip, kh\u00F4ng qua MDC, g\u1ECDn + chu\u1EA9n baseline. -->\n <span class=\"c-token-value c-token-value-edit c-bool-toggle\">\n <button\n type=\"button\"\n class=\"c-bool-btn c-bool-true\"\n [class.c-bool-active]=\"_data === true\"\n [attr.data-autoId]=\"_autoId + '-true'\"\n (click)=\"emitBoolean(true)\">\n {{ $any(_field).trueLabel || booleanTrueLabel() }}\n </button>\n <button\n type=\"button\"\n class=\"c-bool-btn c-bool-false\"\n [class.c-bool-active]=\"_data === false\"\n [attr.data-autoId]=\"_autoId + '-false'\"\n (click)=\"emitBoolean(false)\">\n {{ $any(_field).falseLabel || booleanFalseLabel() }}\n </button>\n </span>\n } @else {\n <button type=\"button\" class=\"c-token-value\" (click)=\"enterEdit()\">{{ valueText() }}</button>\n }\n } @else if ((_field.type === 'date' || _field.type === 'datetime') && _operator === 'BETWEEN') {\n <!-- why: sd-date-range [viewed]=\"'inline'\" t\u1EF1 qu\u1EA3n click\u2192edit + focusout (datetime downgrade v\u1EC1 date).\n Chip kh\u00F4ng wiring lifecycle n\u1EEFa; [clearable]=false v\u00EC chip c\u00F3 \u00D7 ri\u00EAng. -->\n <span class=\"c-token-value c-token-value-edit\">\n <sd-date-range size=\"sm\"\n [autoId]=\"_autoId\"\n [viewed]=\"'inline'\"\n [clearable]=\"false\"\n [model]=\"$any(_data)\"\n (sdChange)=\"emitRangeCommit($any($event))\"></sd-date-range>\n </span>\n } @else if (_field.type === 'values' || _field.type === 'lazy-values') {\n @let _opt = $any(_field).option;\n <!-- why: sd-select [viewed]=\"'inline'\" t\u1EF1 qu\u1EA3n click\u2192edit + focusout\u2192tho\u00E1t + bare editor.\n Chip kh\u00F4ng wiring lifecycle cho nh\u00E1nh n\u00E0y n\u1EEFa (b\u1ECF enterEdit/onFocusOut/bare). -->\n <span class=\"c-token-value c-token-value-edit\">\n <sd-select #chipPicker size=\"sm\" minWidthPanel=\"300px\"\n [autoId]=\"_autoId\"\n [items]=\"_field.type === 'lazy-values' ? $any(_opt).search : $any(_opt.items)\"\n [valueField]=\"_opt.valueField\"\n [displayField]=\"_opt.displayField\"\n [multiple]=\"_multiple\"\n [viewed]=\"'inline'\"\n [clearable]=\"false\"\n [model]=\"$any(_data)\"\n (sdChange)=\"_multiple ? emitLive($event) : emitSingleCommit($event)\">\n <!-- why: viewed mode m\u1EB7c \u0111\u1ECBnh d\u00F9ng comma-joined cho multi \u2014 qu\u00E1 d\u00E0i cho chip.\n Override b\u1EB1ng template \"head +N\" gi\u1ED1ng popover chipValueText. -->\n <ng-template #sdValue let-selectedItems=\"selectedItems\">\n @if ((selectedItems?.length ?? 0) > 1) {\n <span class=\"c-token-multi-head\">{{ $any(selectedItems)[0][_opt.displayField] }}</span> +{{ selectedItems.length - 1 }}\n } @else if ((selectedItems?.length ?? 0) === 1) {\n <span class=\"c-token-multi-head\">{{ $any(selectedItems)[0][_opt.displayField] }}</span>\n }\n </ng-template>\n </sd-select>\n </span>\n } @else if (_field.type === 'date') {\n <span class=\"c-token-value c-token-value-edit\">\n <sd-date size=\"sm\"\n [autoId]=\"_autoId\"\n [viewed]=\"'inline'\"\n [clearable]=\"false\"\n [model]=\"$any(_data)\"\n (sdChange)=\"emitSingleCommit($any($event))\"></sd-date>\n </span>\n } @else if (_field.type === 'datetime') {\n <span class=\"c-token-value c-token-value-edit\">\n <sd-datetime size=\"sm\"\n [autoId]=\"_autoId\"\n [viewed]=\"'inline'\"\n [clearable]=\"false\"\n [model]=\"$any(_data)\"\n (sdChange)=\"emitSingleCommit($any($event))\"></sd-datetime>\n </span>\n } @else {\n <!-- Fallback (unforeseen type) \u2014 read-only display via valueText. -->\n <span class=\"c-token-value\">{{ valueText() }}</span>\n }\n }\n\n <button type=\"button\" class=\"c-token-remove\" aria-label=\"Remove filter\" (click)=\"remove.emit()\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n</div>\n", styles: [":host{display:contents}.c-token{display:inline-flex;align-items:center;gap:4px;padding:0 0 0 12px;border:1px solid var(--sd-primary-light);border-radius:999px;background:var(--sd-primary-light);font-size:13px;color:#5f6368;white-space:nowrap;overflow:hidden}:host-context(.c-density-compact) .c-token{height:28px}:host-context(.c-density-comfortable) .c-token{height:32px}.c-token-icon{font-size:14px;width:14px;height:14px;line-height:14px;color:inherit}.c-token-field{font-weight:500;line-height:1}.c-token-op{display:inline-flex;align-items:center;color:inherit}.c-token-op ::ng-deep .c-op-trigger{padding:0;color:inherit}.c-token-op ::ng-deep .c-op-icon svg{width:14px;height:14px;display:block}.c-token-sep{color:#7a7a7a}.c-token-value{border:none;background:transparent;padding:0;font:inherit;font-weight:500;color:var(--sd-primary);cursor:pointer}.c-token-value:hover{text-decoration:underline}.c-token-value-edit{display:inline-flex;align-items:center;gap:4px;max-width:240px}.c-token-value-edit sd-date-range{display:inline-flex;max-width:220px;min-width:0;flex:0 1 auto}.c-token-value-edit sd-select,.c-token-value-edit sd-date,.c-token-value-edit sd-datetime{display:inline-flex;align-items:center;line-height:1}.c-token ::ng-deep .mat-mdc-form-field{line-height:1}.c-token ::ng-deep .mat-mdc-text-field-wrapper,.c-token ::ng-deep .mat-mdc-form-field-flex{padding:0;min-height:0;background:transparent}.c-token ::ng-deep .mat-mdc-form-field-infix{padding:0;min-height:0;width:auto;border:0;display:inline-flex;align-items:center}.c-token ::ng-deep .mat-mdc-select-value,.c-token ::ng-deep .mat-mdc-input-element,.c-token ::ng-deep input{line-height:1;font-size:13px;padding:0;height:auto;color:inherit}.c-token ::ng-deep .mat-mdc-select-value{display:inline-flex;align-items:center;min-width:0;max-width:200px;overflow:hidden;text-overflow:ellipsis;color:var(--sd-primary);font-weight:500}.c-token ::ng-deep .sd-suffix-icon{display:none}.c-token-multi-head{display:inline-block;max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom}.c-bool-toggle{display:inline-flex;align-items:center;gap:4px}.c-bool-btn{display:inline-flex;align-items:center;justify-content:center;height:18px;min-width:0;padding:0 8px;border:1px solid currentColor;border-radius:999px;background:transparent;font:inherit;font-size:11px;font-weight:500;line-height:1;cursor:pointer;white-space:nowrap;-webkit-user-select:none;user-select:none;transition:background .12s ease,color .12s ease}:host-context(.c-density-comfortable) .c-bool-btn{height:20px;padding:0 10px;font-size:12px}.c-bool-true{color:var(--sd-success)}.c-bool-false{color:var(--sd-warning)}.c-bool-active.c-bool-true{background:var(--sd-success);color:#fff;border-color:var(--sd-success)}.c-bool-active.c-bool-false{background:var(--sd-warning);color:#fff;border-color:var(--sd-warning)}.c-bool-btn:hover:not(.c-bool-active).c-bool-true{background:var(--sd-success-light)}.c-bool-btn:hover:not(.c-bool-active).c-bool-false{background:var(--sd-warning-light)}.c-token-remove{align-self:stretch;display:inline-flex;align-items:center;justify-content:center;border:none;border-left:1px solid rgba(0,0,0,.04);border-radius:0;background:transparent;cursor:pointer;color:inherit}:host-context(.c-density-compact) .c-token-remove{width:26px}:host-context(.c-density-comfortable) .c-token-remove{width:28px}.c-token-remove sd-icon{font-size:14px;width:14px;height:14px}.c-token-remove:hover{background:#0000000d}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "ngmodule", type: CommonModule }, { kind: "component", type: SdOperator, selector: "sd-operator", inputs: ["model", "operators", "disabled", "autoId"], outputs: ["modelChange"] }, { kind: "component", type: SdDate, selector: "sd-date", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "required", "disabled", "viewed", "clearable", "inlineError", "hyperlink", "appearance", "floatLabel", "min", "minDate", "max", "maxDate", "transform", "model"], outputs: ["modelChange", "sdChange", "sdFocus"] }, { kind: "component", type: SdDateRange, selector: "sd-date-range", inputs: ["autoId", "name", "size", "form", "label", "helperText", "hideInlineError", "required", "disabled", "viewed", "clearable", "appearance", "floatLabel", "min", "max", "transform", "model"], outputs: ["modelChange", "sdChange"] }, { kind: "component", type: SdDatetime, selector: "sd-datetime", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "hideInlineError", "required", "disabled", "viewed", "showSeconds", "clearable", "inlineError", "hyperlink", "appearance", "floatLabel", "min", "minDate", "max", "maxDate", "transform", "model"], outputs: ["modelChange", "sdChange", "sdFocus"] }, { kind: "component", type: SdSelect, selector: "sd-select", inputs: ["autoId", "name", "size", "form", "label", "helperText", "placeholder", "valueField", "displayField", "disabledField", "cacheChecksum", "limit", "hyperlink", "minWidthPanel", "hideInlineError", "required", "disabled", "viewed", "multiple", "showSelectAll", "clearable", "validator", "inlineError", "appearance", "floatLabel", "items", "model"], outputs: ["modelChange", "sdChange", "sdSelection"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
865
940
  }
866
941
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdQueryInlineChip, decorators: [{
867
942
  type: Component,
868
- args: [{ selector: 'sd-query-inline-chip', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [SdIcon, CommonModule, SdOperator, SdDate, SdDateRange, SdDatetime, SdSelect], template: "<!--\n Completed inline chip \u2014 non-string/number/seamless branches.\n Edit lifecycle is self-managed: click enters edit + auto-opens picker,\n focusout outside the wrapper exits edit, picker emits commit single OR\n liveChange (multi) OR commitRange (BETWEEN).\n-->\n@let _field = field();\n@let _operator = operator();\n@let _data = data();\n@let _multiple = multiple();\n@let _editing = editing();\n@let _isNoData = isNoData();\n@let _autoId = autoId();\n\n<div class=\"c-token\" [class.c-token-editing]=\"_editing\">\n <sd-icon class=\"c-token-icon\" [name]=\"iconName()\"></sd-icon>\n <span class=\"c-token-field\">{{ _field.label }}</span>\n\n @if (!_isNoData && showOperator()) {\n <sd-operator class=\"c-token-op\" [operators]=\"[_operator]\" [model]=\"_operator\" [disabled]=\"true\" />\n } @else if (!_isNoData) {\n <span class=\"c-token-sep\">:</span>\n }\n\n @if (!_isNoData) {\n @if (_field.type === 'boolean') {\n @if (_editing) {\n <!-- why: native button thay sd-button \u2014 mat-button MDC baseline 36px bu\u1ED9c\n ph\u1EA3i hack height/padding overrides; chip 28/32px nh\u1ECF h\u01A1n nhi\u1EC1u. T\u1EF1 v\u1EBD\n pill nh\u1ECF v\u1EDBi CSS scoped trong chip, kh\u00F4ng qua MDC, g\u1ECDn + chu\u1EA9n baseline. -->\n <span class=\"c-token-value c-token-value-edit c-bool-toggle\">\n <button\n type=\"button\"\n class=\"c-bool-btn c-bool-true\"\n [class.c-bool-active]=\"_data === true\"\n [attr.data-autoId]=\"_autoId + '-true'\"\n (click)=\"emitBoolean(true)\">\n {{ $any(_field).trueLabel || 'C\u00F3' }}\n </button>\n <button\n type=\"button\"\n class=\"c-bool-btn c-bool-false\"\n [class.c-bool-active]=\"_data === false\"\n [attr.data-autoId]=\"_autoId + '-false'\"\n (click)=\"emitBoolean(false)\">\n {{ $any(_field).falseLabel || 'Kh\u00F4ng' }}\n </button>\n </span>\n } @else {\n <button type=\"button\" class=\"c-token-value\" (click)=\"enterEdit()\">{{ valueText() }}</button>\n }\n } @else if ((_field.type === 'date' || _field.type === 'datetime') && _operator === 'BETWEEN') {\n <!-- why: sd-date-range [viewed]=\"'inline'\" t\u1EF1 qu\u1EA3n click\u2192edit + focusout (datetime downgrade v\u1EC1 date).\n Chip kh\u00F4ng wiring lifecycle n\u1EEFa; [clearable]=false v\u00EC chip c\u00F3 \u00D7 ri\u00EAng. -->\n <span class=\"c-token-value c-token-value-edit\">\n <sd-date-range size=\"sm\"\n [autoId]=\"_autoId\"\n [viewed]=\"'inline'\"\n [clearable]=\"false\"\n [model]=\"$any(_data)\"\n (sdChange)=\"emitRangeCommit($any($event))\"></sd-date-range>\n </span>\n } @else if (_field.type === 'values' || _field.type === 'lazy-values') {\n @let _opt = $any(_field).option;\n <!-- why: sd-select [viewed]=\"'inline'\" t\u1EF1 qu\u1EA3n click\u2192edit + focusout\u2192tho\u00E1t + bare editor.\n Chip kh\u00F4ng wiring lifecycle cho nh\u00E1nh n\u00E0y n\u1EEFa (b\u1ECF enterEdit/onFocusOut/bare). -->\n <span class=\"c-token-value c-token-value-edit\">\n <sd-select #chipPicker size=\"sm\" minWidthPanel=\"300px\"\n [autoId]=\"_autoId\"\n [items]=\"_field.type === 'lazy-values' ? $any(_opt).search : $any(_opt.items)\"\n [valueField]=\"_opt.valueField\"\n [displayField]=\"_opt.displayField\"\n [multiple]=\"_multiple\"\n [viewed]=\"'inline'\"\n [clearable]=\"false\"\n [model]=\"$any(_data)\"\n (sdChange)=\"_multiple ? emitLive($event) : emitSingleCommit($event)\">\n <!-- why: viewed mode m\u1EB7c \u0111\u1ECBnh d\u00F9ng comma-joined cho multi \u2014 qu\u00E1 d\u00E0i cho chip.\n Override b\u1EB1ng template \"head +N\" gi\u1ED1ng popover chipValueText. -->\n <ng-template #sdValue let-selectedItems=\"selectedItems\">\n @if ((selectedItems?.length ?? 0) > 1) {\n <span class=\"c-token-multi-head\">{{ $any(selectedItems)[0][_opt.displayField] }}</span> +{{ selectedItems.length - 1 }}\n } @else if ((selectedItems?.length ?? 0) === 1) {\n <span class=\"c-token-multi-head\">{{ $any(selectedItems)[0][_opt.displayField] }}</span>\n }\n </ng-template>\n </sd-select>\n </span>\n } @else if (_field.type === 'date') {\n <span class=\"c-token-value c-token-value-edit\">\n <sd-date size=\"sm\"\n [autoId]=\"_autoId\"\n [viewed]=\"'inline'\"\n [clearable]=\"false\"\n [model]=\"$any(_data)\"\n (sdChange)=\"emitSingleCommit($any($event))\"></sd-date>\n </span>\n } @else if (_field.type === 'datetime') {\n <span class=\"c-token-value c-token-value-edit\">\n <sd-datetime size=\"sm\"\n [autoId]=\"_autoId\"\n [viewed]=\"'inline'\"\n [clearable]=\"false\"\n [model]=\"$any(_data)\"\n (sdChange)=\"emitSingleCommit($any($event))\"></sd-datetime>\n </span>\n } @else {\n <!-- Fallback (unforeseen type) \u2014 read-only display via valueText. -->\n <span class=\"c-token-value\">{{ valueText() }}</span>\n }\n }\n\n <button type=\"button\" class=\"c-token-remove\" aria-label=\"Remove filter\" (click)=\"remove.emit()\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n</div>\n", styles: [":host{display:contents}.c-token{display:inline-flex;align-items:center;gap:4px;padding:0 0 0 12px;border:1px solid var(--sd-primary-light);border-radius:999px;background:var(--sd-primary-light);font-size:13px;color:#5f6368;white-space:nowrap;overflow:hidden}:host-context(.c-density-compact) .c-token{height:28px}:host-context(.c-density-comfortable) .c-token{height:32px}.c-token-icon{font-size:14px;width:14px;height:14px;line-height:14px;color:inherit}.c-token-field{font-weight:500;line-height:1}.c-token-op{display:inline-flex;align-items:center;color:inherit}.c-token-op ::ng-deep .c-op-trigger{padding:0;color:inherit}.c-token-op ::ng-deep .c-op-icon svg{width:14px;height:14px;display:block}.c-token-sep{color:#7a7a7a}.c-token-value{border:none;background:transparent;padding:0;font:inherit;font-weight:500;color:var(--sd-primary);cursor:pointer}.c-token-value:hover{text-decoration:underline}.c-token-value-edit{display:inline-flex;align-items:center;gap:4px;max-width:240px}.c-token-value-edit sd-date-range{display:inline-flex;max-width:220px;min-width:0;flex:0 1 auto}.c-token-value-edit sd-select,.c-token-value-edit sd-date,.c-token-value-edit sd-datetime{display:inline-flex;align-items:center;line-height:1}.c-token ::ng-deep .mat-mdc-form-field{line-height:1}.c-token ::ng-deep .mat-mdc-text-field-wrapper,.c-token ::ng-deep .mat-mdc-form-field-flex{padding:0;min-height:0;background:transparent}.c-token ::ng-deep .mat-mdc-form-field-infix{padding:0;min-height:0;width:auto;border:0;display:inline-flex;align-items:center}.c-token ::ng-deep .mat-mdc-select-value,.c-token ::ng-deep .mat-mdc-input-element,.c-token ::ng-deep input{line-height:1;font-size:13px;padding:0;height:auto;color:inherit}.c-token ::ng-deep .mat-mdc-select-value{display:inline-flex;align-items:center;min-width:0;max-width:200px;overflow:hidden;text-overflow:ellipsis;color:var(--sd-primary);font-weight:500}.c-token ::ng-deep .sd-suffix-icon{display:none}.c-token-multi-head{display:inline-block;max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom}.c-bool-toggle{display:inline-flex;align-items:center;gap:4px}.c-bool-btn{display:inline-flex;align-items:center;justify-content:center;height:18px;min-width:0;padding:0 8px;border:1px solid currentColor;border-radius:999px;background:transparent;font:inherit;font-size:11px;font-weight:500;line-height:1;cursor:pointer;white-space:nowrap;-webkit-user-select:none;user-select:none;transition:background .12s ease,color .12s ease}:host-context(.c-density-comfortable) .c-bool-btn{height:20px;padding:0 10px;font-size:12px}.c-bool-true{color:var(--sd-success)}.c-bool-false{color:var(--sd-warning)}.c-bool-active.c-bool-true{background:var(--sd-success);color:#fff;border-color:var(--sd-success)}.c-bool-active.c-bool-false{background:var(--sd-warning);color:#fff;border-color:var(--sd-warning)}.c-bool-btn:hover:not(.c-bool-active).c-bool-true{background:var(--sd-success-light)}.c-bool-btn:hover:not(.c-bool-active).c-bool-false{background:var(--sd-warning-light)}.c-token-remove{align-self:stretch;display:inline-flex;align-items:center;justify-content:center;border:none;border-left:1px solid rgba(0,0,0,.04);border-radius:0;background:transparent;cursor:pointer;color:inherit}:host-context(.c-density-compact) .c-token-remove{width:26px}:host-context(.c-density-comfortable) .c-token-remove{width:28px}.c-token-remove sd-icon{font-size:14px;width:14px;height:14px}.c-token-remove:hover{background:#0000000d}\n"] }]
943
+ args: [{ selector: 'sd-query-inline-chip', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [SdIcon, CommonModule, SdOperator, SdDate, SdDateRange, SdDatetime, SdSelect], template: "<!--\n Completed inline chip \u2014 non-string/number/seamless branches.\n Edit lifecycle is self-managed: click enters edit + auto-opens picker,\n focusout outside the wrapper exits edit, picker emits commit single OR\n liveChange (multi) OR commitRange (BETWEEN).\n-->\n@let _field = field();\n@let _operator = operator();\n@let _data = data();\n@let _multiple = multiple();\n@let _editing = editing();\n@let _isNoData = isNoData();\n@let _autoId = autoId();\n\n<div class=\"c-token\" [class.c-token-editing]=\"_editing\">\n <sd-icon class=\"c-token-icon\" [name]=\"iconName()\"></sd-icon>\n <span class=\"c-token-field\">{{ _field.label }}</span>\n\n @if (!_isNoData && showOperator()) {\n <sd-operator class=\"c-token-op\" [operators]=\"[_operator]\" [model]=\"_operator\" [disabled]=\"true\" />\n } @else if (!_isNoData) {\n <span class=\"c-token-sep\">:</span>\n }\n\n @if (!_isNoData) {\n @if (_field.type === 'boolean') {\n @if (_editing) {\n <!-- why: native button thay sd-button \u2014 mat-button MDC baseline 36px bu\u1ED9c\n ph\u1EA3i hack height/padding overrides; chip 28/32px nh\u1ECF h\u01A1n nhi\u1EC1u. T\u1EF1 v\u1EBD\n pill nh\u1ECF v\u1EDBi CSS scoped trong chip, kh\u00F4ng qua MDC, g\u1ECDn + chu\u1EA9n baseline. -->\n <span class=\"c-token-value c-token-value-edit c-bool-toggle\">\n <button\n type=\"button\"\n class=\"c-bool-btn c-bool-true\"\n [class.c-bool-active]=\"_data === true\"\n [attr.data-autoId]=\"_autoId + '-true'\"\n (click)=\"emitBoolean(true)\">\n {{ $any(_field).trueLabel || booleanTrueLabel() }}\n </button>\n <button\n type=\"button\"\n class=\"c-bool-btn c-bool-false\"\n [class.c-bool-active]=\"_data === false\"\n [attr.data-autoId]=\"_autoId + '-false'\"\n (click)=\"emitBoolean(false)\">\n {{ $any(_field).falseLabel || booleanFalseLabel() }}\n </button>\n </span>\n } @else {\n <button type=\"button\" class=\"c-token-value\" (click)=\"enterEdit()\">{{ valueText() }}</button>\n }\n } @else if ((_field.type === 'date' || _field.type === 'datetime') && _operator === 'BETWEEN') {\n <!-- why: sd-date-range [viewed]=\"'inline'\" t\u1EF1 qu\u1EA3n click\u2192edit + focusout (datetime downgrade v\u1EC1 date).\n Chip kh\u00F4ng wiring lifecycle n\u1EEFa; [clearable]=false v\u00EC chip c\u00F3 \u00D7 ri\u00EAng. -->\n <span class=\"c-token-value c-token-value-edit\">\n <sd-date-range size=\"sm\"\n [autoId]=\"_autoId\"\n [viewed]=\"'inline'\"\n [clearable]=\"false\"\n [model]=\"$any(_data)\"\n (sdChange)=\"emitRangeCommit($any($event))\"></sd-date-range>\n </span>\n } @else if (_field.type === 'values' || _field.type === 'lazy-values') {\n @let _opt = $any(_field).option;\n <!-- why: sd-select [viewed]=\"'inline'\" t\u1EF1 qu\u1EA3n click\u2192edit + focusout\u2192tho\u00E1t + bare editor.\n Chip kh\u00F4ng wiring lifecycle cho nh\u00E1nh n\u00E0y n\u1EEFa (b\u1ECF enterEdit/onFocusOut/bare). -->\n <span class=\"c-token-value c-token-value-edit\">\n <sd-select #chipPicker size=\"sm\" minWidthPanel=\"300px\"\n [autoId]=\"_autoId\"\n [items]=\"_field.type === 'lazy-values' ? $any(_opt).search : $any(_opt.items)\"\n [valueField]=\"_opt.valueField\"\n [displayField]=\"_opt.displayField\"\n [multiple]=\"_multiple\"\n [viewed]=\"'inline'\"\n [clearable]=\"false\"\n [model]=\"$any(_data)\"\n (sdChange)=\"_multiple ? emitLive($event) : emitSingleCommit($event)\">\n <!-- why: viewed mode m\u1EB7c \u0111\u1ECBnh d\u00F9ng comma-joined cho multi \u2014 qu\u00E1 d\u00E0i cho chip.\n Override b\u1EB1ng template \"head +N\" gi\u1ED1ng popover chipValueText. -->\n <ng-template #sdValue let-selectedItems=\"selectedItems\">\n @if ((selectedItems?.length ?? 0) > 1) {\n <span class=\"c-token-multi-head\">{{ $any(selectedItems)[0][_opt.displayField] }}</span> +{{ selectedItems.length - 1 }}\n } @else if ((selectedItems?.length ?? 0) === 1) {\n <span class=\"c-token-multi-head\">{{ $any(selectedItems)[0][_opt.displayField] }}</span>\n }\n </ng-template>\n </sd-select>\n </span>\n } @else if (_field.type === 'date') {\n <span class=\"c-token-value c-token-value-edit\">\n <sd-date size=\"sm\"\n [autoId]=\"_autoId\"\n [viewed]=\"'inline'\"\n [clearable]=\"false\"\n [model]=\"$any(_data)\"\n (sdChange)=\"emitSingleCommit($any($event))\"></sd-date>\n </span>\n } @else if (_field.type === 'datetime') {\n <span class=\"c-token-value c-token-value-edit\">\n <sd-datetime size=\"sm\"\n [autoId]=\"_autoId\"\n [viewed]=\"'inline'\"\n [clearable]=\"false\"\n [model]=\"$any(_data)\"\n (sdChange)=\"emitSingleCommit($any($event))\"></sd-datetime>\n </span>\n } @else {\n <!-- Fallback (unforeseen type) \u2014 read-only display via valueText. -->\n <span class=\"c-token-value\">{{ valueText() }}</span>\n }\n }\n\n <button type=\"button\" class=\"c-token-remove\" aria-label=\"Remove filter\" (click)=\"remove.emit()\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n</div>\n", styles: [":host{display:contents}.c-token{display:inline-flex;align-items:center;gap:4px;padding:0 0 0 12px;border:1px solid var(--sd-primary-light);border-radius:999px;background:var(--sd-primary-light);font-size:13px;color:#5f6368;white-space:nowrap;overflow:hidden}:host-context(.c-density-compact) .c-token{height:28px}:host-context(.c-density-comfortable) .c-token{height:32px}.c-token-icon{font-size:14px;width:14px;height:14px;line-height:14px;color:inherit}.c-token-field{font-weight:500;line-height:1}.c-token-op{display:inline-flex;align-items:center;color:inherit}.c-token-op ::ng-deep .c-op-trigger{padding:0;color:inherit}.c-token-op ::ng-deep .c-op-icon svg{width:14px;height:14px;display:block}.c-token-sep{color:#7a7a7a}.c-token-value{border:none;background:transparent;padding:0;font:inherit;font-weight:500;color:var(--sd-primary);cursor:pointer}.c-token-value:hover{text-decoration:underline}.c-token-value-edit{display:inline-flex;align-items:center;gap:4px;max-width:240px}.c-token-value-edit sd-date-range{display:inline-flex;max-width:220px;min-width:0;flex:0 1 auto}.c-token-value-edit sd-select,.c-token-value-edit sd-date,.c-token-value-edit sd-datetime{display:inline-flex;align-items:center;line-height:1}.c-token ::ng-deep .mat-mdc-form-field{line-height:1}.c-token ::ng-deep .mat-mdc-text-field-wrapper,.c-token ::ng-deep .mat-mdc-form-field-flex{padding:0;min-height:0;background:transparent}.c-token ::ng-deep .mat-mdc-form-field-infix{padding:0;min-height:0;width:auto;border:0;display:inline-flex;align-items:center}.c-token ::ng-deep .mat-mdc-select-value,.c-token ::ng-deep .mat-mdc-input-element,.c-token ::ng-deep input{line-height:1;font-size:13px;padding:0;height:auto;color:inherit}.c-token ::ng-deep .mat-mdc-select-value{display:inline-flex;align-items:center;min-width:0;max-width:200px;overflow:hidden;text-overflow:ellipsis;color:var(--sd-primary);font-weight:500}.c-token ::ng-deep .sd-suffix-icon{display:none}.c-token-multi-head{display:inline-block;max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom}.c-bool-toggle{display:inline-flex;align-items:center;gap:4px}.c-bool-btn{display:inline-flex;align-items:center;justify-content:center;height:18px;min-width:0;padding:0 8px;border:1px solid currentColor;border-radius:999px;background:transparent;font:inherit;font-size:11px;font-weight:500;line-height:1;cursor:pointer;white-space:nowrap;-webkit-user-select:none;user-select:none;transition:background .12s ease,color .12s ease}:host-context(.c-density-comfortable) .c-bool-btn{height:20px;padding:0 10px;font-size:12px}.c-bool-true{color:var(--sd-success)}.c-bool-false{color:var(--sd-warning)}.c-bool-active.c-bool-true{background:var(--sd-success);color:#fff;border-color:var(--sd-success)}.c-bool-active.c-bool-false{background:var(--sd-warning);color:#fff;border-color:var(--sd-warning)}.c-bool-btn:hover:not(.c-bool-active).c-bool-true{background:var(--sd-success-light)}.c-bool-btn:hover:not(.c-bool-active).c-bool-false{background:var(--sd-warning-light)}.c-token-remove{align-self:stretch;display:inline-flex;align-items:center;justify-content:center;border:none;border-left:1px solid rgba(0,0,0,.04);border-radius:0;background:transparent;cursor:pointer;color:inherit}:host-context(.c-density-compact) .c-token-remove{width:26px}:host-context(.c-density-comfortable) .c-token-remove{width:28px}.c-token-remove sd-icon{font-size:14px;width:14px;height:14px}.c-token-remove:hover{background:#0000000d}\n"] }]
869
944
  }], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: true }] }], density: [{ type: i0.Input, args: [{ isSignal: true, alias: "density", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], showOperator: [{ type: i0.Input, args: [{ isSignal: true, alias: "showOperator", required: false }] }], autoId: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoId", required: false }] }], valueText: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueText", required: false }] }], isNoData: [{ type: i0.Input, args: [{ isSignal: true, alias: "isNoData", required: false }] }], commit: [{ type: i0.Output, args: ["commit"] }], commitRange: [{ type: i0.Output, args: ["commitRange"] }], liveChange: [{ type: i0.Output, args: ["liveChange"] }], remove: [{ type: i0.Output, args: ["remove"] }] } });
870
945
 
871
946
  /**
@@ -1010,6 +1085,19 @@ class SdQueryBar {
1010
1085
  // ---------------------------------------------------------------------------
1011
1086
  // Derived state
1012
1087
  // ---------------------------------------------------------------------------
1088
+ // ---------------------------------------------------------------------------
1089
+ // Nhãn i18n cho template
1090
+ // why: `#i18n` trước đây được inject nhưng KHÔNG dùng — mọi chuỗi trong template đều hardcode
1091
+ // tiếng Việt. Bọc qua `computed()` thay vì pipe `translate` vì pipe là pure: nó chỉ chạy lại khi
1092
+ // input đổi tham chiếu, nên đổi ngôn ngữ runtime không refresh được. `t()` đọc signal `messages()`
1093
+ // nên computed tự invalidate khi `I18nService.setLanguage()` chạy.
1094
+ // ---------------------------------------------------------------------------
1095
+ /** Placeholder của ô tìm kiếm tự do bên trái. */
1096
+ searchPlaceholder = computed(() => this.#i18n.t('core.component.query-bar.search-placeholder'), ...(ngDevMode ? [{ debugName: "searchPlaceholder" }] : /* istanbul ignore next */ []));
1097
+ /** Tooltip nút "+" khi consumer chưa khai `fields`. */
1098
+ noFieldsLabel = computed(() => this.#i18n.t('core.component.query-bar.no-fields'), ...(ngDevMode ? [{ debugName: "noFieldsLabel" }] : /* istanbul ignore next */ []));
1099
+ /** Tooltip nút "+" ở trạng thái bình thường. */
1100
+ addFilterLabel = computed(() => this.#i18n.t('core.component.query-bar.add-filter'), ...(ngDevMode ? [{ debugName: "addFilterLabel" }] : /* istanbul ignore next */ []));
1013
1101
  /** Map of `field.key` → `SdQueryField` for fast lookup from filters[]. */
1014
1102
  resolvedAutoId = computed(() => this.option()?.autoId ?? this.autoIdInput() ?? undefined, ...(ngDevMode ? [{ debugName: "resolvedAutoId" }] : /* istanbul ignore next */ []));
1015
1103
  resolvedFields = computed(() => this.option()?.fields ?? this.fields(), ...(ngDevMode ? [{ debugName: "resolvedFields" }] : /* istanbul ignore next */ []));
@@ -1080,6 +1168,52 @@ class SdQueryBar {
1080
1168
  return SD_QUERY_MULTI_OPERATORS.includes(op);
1081
1169
  }
1082
1170
  // ---------------------------------------------------------------------------
1171
+ // Row identity — stable synthetic id per filter chip, used as the `@for` track key
1172
+ // ---------------------------------------------------------------------------
1173
+ /**
1174
+ * Filter object → synthetic row id.
1175
+ *
1176
+ * why: `@for (... ; track $index)` gắn chip với VỊ TRÍ, không phải với filter. Xoá chip index 0
1177
+ * thì chip index 0 không bị huỷ mà được bind lại sang filter kế tiếp, kéo theo state nội bộ
1178
+ * của chip (`inline-chip` `#editing`, `inline-value-chip` `draft`) sang một filter khác.
1179
+ * Gắn id tổng hợp cho từng dòng để `track` bám đúng filter.
1180
+ *
1181
+ * Lưu trong WeakMap thay vì gắn thẳng thuộc tính lên `Filter` để payload `SdQuery.filters` phát
1182
+ * ra ngoài vẫn sạch (không rò field nội bộ cho consumer).
1183
+ */
1184
+ #rowIds = new WeakMap();
1185
+ #rowIdSeq = 0;
1186
+ /**
1187
+ * Stable track key of a filter row. Assigned lazily on first read.
1188
+ *
1189
+ * ⚠️ Hai ràng buộc với `filters`, đều là hợp đồng của consumer:
1190
+ *
1191
+ * 1. KHÔNG được chứa cùng một object `Filter` ở hai vị trí. Khoá theo identity nên hai vị trí sẽ
1192
+ * cho hai track key trùng nhau và Angular ném NG0955. Đã thử khử trùng bằng hậu tố `$index`
1193
+ * và HỎNG: Angular đánh giá biểu thức `track` trên CẢ collection cũ khi diff, nên một filter
1194
+ * vừa bị xoá cho `indexOf === -1`, key đổi, và MỌI chip bị huỷ rồi dựng lại. Để Angular báo
1195
+ * NG0955 là đúng — thông điệp của nó đã chỉ rõ vấn đề.
1196
+ * 2. Object phải ỔN ĐỊNH giữa các lần change detection. Sinh lại object mới mỗi pass
1197
+ * (vd `[option]="{ filters: buildFilters() }"`) sẽ khiến mọi chip nhận id mới mỗi pass và bị
1198
+ * huỷ + dựng lại — tệ hơn cả `track $index` cũ.
1199
+ */
1200
+ rowId(filter) {
1201
+ let id = this.#rowIds.get(filter);
1202
+ if (!id) {
1203
+ id = `qbr-${++this.#rowIdSeq}`;
1204
+ this.#rowIds.set(filter, id);
1205
+ }
1206
+ return id;
1207
+ }
1208
+ /**
1209
+ * Chuyển id của một dòng sang object thay thế.
1210
+ * why: mọi mutation đều tạo object mới (`{ ...previous, ...patch }`); không kế thừa id thì mỗi
1211
+ * lần sửa giá trị lại huỷ + dựng lại chip, mất focus và state đang gõ dở.
1212
+ */
1213
+ #inheritRowId(previous, next) {
1214
+ this.#rowIds.set(next, this.rowId(previous));
1215
+ }
1216
+ // ---------------------------------------------------------------------------
1083
1217
  // Template helpers
1084
1218
  // ---------------------------------------------------------------------------
1085
1219
  iconFor = sdQueryFieldIcon;
@@ -1121,6 +1255,12 @@ class SdQueryBar {
1121
1255
  /** The single in-progress inline chip (not yet in `filters`). */
1122
1256
  #building = signal(null, ...(ngDevMode ? [{ debugName: "#building" }] : /* istanbul ignore next */ []));
1123
1257
  building = this.#building.asReadonly();
1258
+ /**
1259
+ * Allowed operators of `field` — safe to call from a template binding: `sdQueryAllowedOperators`
1260
+ * memo hoá theo object field nên ref trả về ổn định giữa các chu kỳ CD.
1261
+ * why: `<sd-query-build-chip [allowedOperators]>` là OnPush; ref mới mỗi CD sẽ dirty child liên
1262
+ * tục (bug class cấp-phát-mỗi-CD).
1263
+ */
1124
1264
  allowedOperatorsFor(field) {
1125
1265
  return sdQueryAllowedOperators(field);
1126
1266
  }
@@ -1247,7 +1387,12 @@ class SdQueryBar {
1247
1387
  const list = [...this.filters()];
1248
1388
  if (index < 0 || index >= list.length)
1249
1389
  return;
1250
- list[index] = { ...list[index], ...patch };
1390
+ const previous = list[index];
1391
+ const next = { ...previous, ...patch };
1392
+ // why: cùng một dòng logic, chỉ đổi data/operator → giữ nguyên track key để chip được patch
1393
+ // tại chỗ thay vì huỷ + dựng lại.
1394
+ this.#inheritRowId(previous, next);
1395
+ list[index] = next;
1251
1396
  this.filters.set(list);
1252
1397
  }
1253
1398
  updateFilterData(index, data) {
@@ -1257,10 +1402,19 @@ class SdQueryBar {
1257
1402
  const list = [...this.filters()];
1258
1403
  if (index < 0 || index >= list.length)
1259
1404
  return;
1260
- // close the open popover (if it's this chip's) before the chip + its trigger vanish
1261
- if (this.editingIndex() === index) {
1262
- this.popoverChips()[index]?.closeMenu();
1263
- this.editingIndex.set(null);
1405
+ const editing = this.editingIndex();
1406
+ // why: `editingIndex` chỉ số vào mảng filters, nên mọi lần xoá ở vị trí <= nó đều làm nó
1407
+ // lệch. Trước đây chỉ xử lý trường hợp bằng nhau → xoá một chip ĐỨNG TRƯỚC khiến editingIndex
1408
+ // trỏ lố 1 ô và `onChipPopoverCommit` ghi giá trị staged vào SAI filter.
1409
+ if (editing !== null && index <= editing) {
1410
+ if (index === editing) {
1411
+ // close the open popover (it's this chip's) before the chip + its trigger vanish
1412
+ this.popoverChips()[index]?.closeMenu();
1413
+ this.editingIndex.set(null);
1414
+ }
1415
+ else {
1416
+ this.editingIndex.set(editing - 1);
1417
+ }
1264
1418
  }
1265
1419
  list.splice(index, 1);
1266
1420
  this.filters.set(list);
@@ -1311,6 +1465,12 @@ class SdQueryBar {
1311
1465
  if (idx === null)
1312
1466
  return;
1313
1467
  const list = [...this.filters()];
1468
+ const previous = list[idx];
1469
+ if (!previous)
1470
+ return;
1471
+ // why: popover chỉ đổi operator + value của CHÍNH chip đó → giữ track key, nếu không chip sẽ
1472
+ // bị huỷ ngay lúc menu đóng (trigger của overlay biến mất giữa chừng).
1473
+ this.#inheritRowId(previous, next);
1314
1474
  list[idx] = next;
1315
1475
  this.filters.set(list);
1316
1476
  this.editingIndex.set(null);
@@ -1331,8 +1491,10 @@ class SdQueryBar {
1331
1491
  if (!field)
1332
1492
  return String(raw);
1333
1493
  if (field.type === 'boolean') {
1334
- const trueLabel = field.trueLabel ?? 'Có';
1335
- const falseLabel = field.falseLabel ?? 'Không';
1494
+ // why: nhãn mặc định của field boolean phải theo ngôn ngữ đang chọn; consumer vẫn override
1495
+ // được bằng `trueLabel` / `falseLabel` trên chính field.
1496
+ const trueLabel = field.trueLabel ?? this.#i18n.t('core.component.query-bar.boolean.true');
1497
+ const falseLabel = field.falseLabel ?? this.#i18n.t('core.component.query-bar.boolean.false');
1336
1498
  return raw ? trueLabel : falseLabel;
1337
1499
  }
1338
1500
  if (field.type === 'values' || field.type === 'lazy-values') {
@@ -1355,7 +1517,7 @@ class SdQueryBar {
1355
1517
  return null;
1356
1518
  }
1357
1519
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdQueryBar, deps: [], target: i0.ɵɵFactoryTarget.Component });
1358
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SdQueryBar, isStandalone: true, selector: "sd-query-bar", inputs: { option: { classPropertyName: "option", publicName: "option", isSignal: true, isRequired: false, transformFunction: null }, autoIdInput: { classPropertyName: "autoIdInput", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, fields: { classPropertyName: "fields", publicName: "fields", isSignal: true, isRequired: false, transformFunction: null }, filters: { classPropertyName: "filters", publicName: "filters", isSignal: true, isRequired: false, transformFunction: null }, logic: { classPropertyName: "logic", publicName: "logic", isSignal: true, isRequired: false, transformFunction: null }, search: { classPropertyName: "search", publicName: "search", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, density: { classPropertyName: "density", publicName: "density", isSignal: true, isRequired: false, transformFunction: null }, showSearch: { classPropertyName: "showSearch", publicName: "showSearch", isSignal: true, isRequired: false, transformFunction: null }, showSavedFilters: { classPropertyName: "showSavedFilters", publicName: "showSavedFilters", isSignal: true, isRequired: false, transformFunction: null }, savedFiltersKey: { classPropertyName: "savedFiltersKey", publicName: "savedFiltersKey", isSignal: true, isRequired: false, transformFunction: null }, showLogicToggle: { classPropertyName: "showLogicToggle", publicName: "showLogicToggle", isSignal: true, isRequired: false, transformFunction: null }, showClearAll: { classPropertyName: "showClearAll", publicName: "showClearAll", isSignal: true, isRequired: false, transformFunction: null }, showOperatorOnChip: { classPropertyName: "showOperatorOnChip", publicName: "showOperatorOnChip", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { filters: "filtersChange", logic: "logicChange", search: "searchChange", queryChange: "queryChange", apply: "apply" }, viewQueries: [{ propertyName: "popoverChips", predicate: SdQueryPopoverChip, descendants: true, isSignal: true }, { propertyName: "buildChip", first: true, predicate: SdQueryBuildChip, descendants: true, isSignal: true }, { propertyName: "chipPopover", first: true, predicate: SdQueryChipPopover, descendants: true, isSignal: true }], ngImport: i0, template: "<!--\n sd-query-bar \u2014 unified filter chip row (Jira/Linear/Notion style).\n Visual spec: refs/design_handoff_sd_query_bar/README.md\n TODO(next-iter): replace inline placeholders for field-picker + chip-popover\n with extracted sub-components driven by CDK overlay.\n-->\n@let _filters = filters();\n@let _resolvedFields = resolvedFields();\n@let _resolvedDensity = resolvedDensity();\n@let _showOrConnector = showOrConnector();\n@let _usedFieldKeys = usedFieldKeys();\n@let _editingIndex = editingIndex();\n@let _resolvedMode = resolvedMode();\n@let _resolvedShowOperatorOnChip = resolvedShowOperatorOnChip();\n\n<div\n class=\"c-query-bar\"\n [class.c-density-compact]=\"_resolvedDensity === 'compact'\"\n [class.c-density-comfortable]=\"_resolvedDensity === 'comfortable'\">\n\n <!-- Main region \u2014 search + filters + add-filter. Wraps internally; the right\n action toolbar stays anchored and does NOT flow with this. -->\n <div class=\"c-query-bar__main\">\n\n <!-- Free-text search (left, optional) -->\n @if (resolvedShowSearch()) {\n <div class=\"c-search\">\n <sd-icon class=\"c-search-icon\" name=\"search\"></sd-icon>\n <input\n class=\"c-search-input\"\n type=\"text\"\n [ngModel]=\"search()\"\n (ngModelChange)=\"setSearch($event)\"\n (keydown.enter)=\"triggerApply()\"\n placeholder=\"T\u00ECm ki\u1EBFm...\" />\n </div>\n <span class=\"c-divider\" aria-hidden=\"true\"></span>\n }\n\n <!-- Chip popover \u2014 extracted into <sd-query-chip-popover>. Declared BEFORE chips\n so the popover-mode @for chips can bind their [menu] to `chipPopover.menu()`.\n why: m\u1EDF popover \u0111\u1EC3 EDIT ch\u1EC9 cho ch\u1EC9nh operator + value \u2014 KH\u00D4NG \u0111\u1ED5i field.\n B\u1ECF [switchPickerMenu] \u2192 header render nh\u00E3n field t\u0129nh (read-only). Mu\u1ED1n \u0111\u1ED5i\n field th\u00EC xo\u00E1 chip r\u1ED3i th\u00EAm l\u1EA1i. -->\n <sd-query-chip-popover\n #chipPopover\n [field]=\"editingField() ?? undefined\"\n [filter]=\"_editingIndex !== null ? $any(_filters[_editingIndex!]) : undefined\"\n [chipIndex]=\"_editingIndex\"\n [autoIdBase]=\"resolvedAutoId() ?? 'qb'\"\n (commit)=\"onChipPopoverCommit($event)\" />\n\n @if (_resolvedMode === 'inline') {\n <!-- ===== INLINE MODE (GitLab-style token builder) ===== -->\n\n <!-- Completed chips -->\n @for (filter of _filters; track $index; let i = $index) {\n @if (_showOrConnector && i > 0) {\n <span class=\"c-or-connector\" aria-hidden=\"true\">OR</span>\n }\n @let _field = fieldByKey()[$any(filter).field];\n @if (_field) {\n @let _op = $any(filter.operator);\n @let _data = $any(filter).data;\n @if (_field.type === 'string' || _field.type === 'number') {\n <!-- Seamless chip \u2014 the pill is the input (string / number). -->\n <sd-query-inline-value-chip\n [field]=\"_field\"\n [operator]=\"_op\"\n [value]=\"_data\"\n [density]=\"_resolvedDensity\"\n [showOperator]=\"_resolvedShowOperatorOnChip\"\n [autoId]=\"inlineAutoId(i, 'value')\"\n (valueChange)=\"updateFilterData(i, $event)\"\n (remove)=\"removeFilter(i)\" />\n } @else {\n <sd-query-inline-chip\n [field]=\"_field\"\n [filter]=\"filter\"\n [density]=\"_resolvedDensity\"\n [multiple]=\"multiple(_op)\"\n [showOperator]=\"_resolvedShowOperatorOnChip\"\n [autoId]=\"inlineAutoId(i, 'value')\"\n [valueText]=\"chipValueText(filter)\"\n [isNoData]=\"isNoDataOperator(_op)\"\n (commit)=\"updateFilterData(i, $event)\"\n (commitRange)=\"setFilterRange(i, $event)\"\n (liveChange)=\"updateFilterData(i, $event)\"\n (remove)=\"removeFilter(i)\" />\n }\n }\n }\n\n <!-- Building chip (in-progress) \u2014 extracted into <sd-query-build-chip>. -->\n @if (building(); as _b) {\n <sd-query-build-chip\n [building]=\"_b\"\n [density]=\"_resolvedDensity\"\n [allowedOperators]=\"allowedOperatorsFor(_b.field)\"\n [multiple]=\"multiple($any(_b.operator))\"\n [showOperator]=\"_resolvedShowOperatorOnChip\"\n (pickOperator)=\"pickBuildOperator($any($event))\"\n (commitValue)=\"commitBuildValue($event)\"\n (cancel)=\"cancelBuild()\"\n (seamlessCommit)=\"onBuildSeamlessCommit($event)\" />\n }\n\n <!-- Add filter -->\n <button\n type=\"button\"\n class=\"c-add-filter\"\n [matMenuTriggerFor]=\"addPicker.menu()\"\n [disabled]=\"_resolvedFields.length === 0 || _usedFieldKeys.size >= _resolvedFields.length\"\n [matTooltip]=\"_resolvedFields.length === 0 ? 'Ch\u01B0a c\u1EA5u h\u00ECnh fields' : 'Th\u00EAm filter'\">\n <sd-icon name=\"add\"></sd-icon>\n </button>\n } @else {\n <!-- ===== POPOVER MODE (default) \u2014 compact chips edited via mat-menu ===== -->\n @for (filter of _filters; track $index; let i = $index) {\n @if (_showOrConnector && i > 0) {\n <span class=\"c-or-connector\" aria-hidden=\"true\">OR</span>\n }\n @let _field = fieldByKey()[$any(filter).field];\n @let _active = isFilterActive(filter);\n <!-- why: menu close commit is handled by chipPopover's (commit) \u2192\n onChipPopoverCommit; popover-chip only signals open + remove here. -->\n <sd-query-popover-chip\n [field]=\"_field\"\n [filter]=\"filter\"\n [active]=\"_active\"\n [showOperator]=\"_resolvedShowOperatorOnChip\"\n [valueText]=\"chipValueText(filter)\"\n [menu]=\"chipPopover.menu()!\"\n (open)=\"openChipPopover(i)\"\n (remove)=\"removeFilter(i)\" />\n }\n\n <!-- Add filter trigger -->\n <button\n type=\"button\"\n class=\"c-add-filter\"\n [matMenuTriggerFor]=\"addPicker.menu()\"\n [disabled]=\"_resolvedFields.length === 0 || _usedFieldKeys.size >= _resolvedFields.length\"\n [matTooltip]=\"_resolvedFields.length === 0 ? 'Ch\u01B0a c\u1EA5u h\u00ECnh fields' : 'Th\u00EAm filter'\">\n <sd-icon name=\"add\"></sd-icon>\n </button>\n }\n\n </div><!-- /.c-query-bar__main -->\n\n <!-- Field picker \u2014 list available fields, fade ones already in use. -->\n <sd-query-field-picker\n #addPicker\n [fields]=\"_resolvedFields\"\n [usedKeys]=\"_usedFieldKeys\"\n (pick)=\"_resolvedMode === 'inline' ? beginBuild($event) : addFilter($event)\" />\n\n <sd-query-actions-bar\n [showLogicToggle]=\"resolvedShowLogicToggle()\"\n [(logic)]=\"logic\"\n [filtersCount]=\"_filters.length\"\n [showSavedFilters]=\"resolvedShowSavedFilters()\"\n [savedFiltersKey]=\"resolvedSavedFiltersKey()\"\n [query]=\"currentQuery()\"\n (applyFilter)=\"onApplyFilter($event)\"\n [showClearAll]=\"resolvedShowClearAll()\"\n [canSearch]=\"canSearch()\"\n (clear)=\"clearAll()\"\n (search)=\"triggerApply()\" />\n</div>\n", styles: [":host{display:block;font-family:Roboto,system-ui,sans-serif}.c-query-bar{display:flex;align-items:flex-start;flex-wrap:nowrap;gap:8px;background:#fff;border:1px solid #e2e4e7;border-radius:8px;box-shadow:0 1px 2px #0f172a0a}.c-query-bar.c-density-compact{padding:8px}.c-query-bar.c-density-comfortable{padding:10px}.c-query-bar__main{flex:1 1 auto;min-width:0;display:flex;align-items:center;flex-wrap:wrap;gap:6px}.c-search{display:flex;align-items:center;gap:6px;min-width:220px;padding:0 10px;border:1px solid #d6d8db;border-radius:6px;background:#fff}.c-density-compact .c-search{height:28px}.c-density-comfortable .c-search{height:32px}.c-search .c-search-icon{font-size:16px;width:16px;height:16px;color:#7a7a7a}.c-search .c-search-input{flex:1;border:none;outline:none;background:transparent;font-size:13px;color:#212121}.c-search .c-search-input::placeholder{color:#7a7a7a}.c-search .c-search-kbd{font-family:Roboto Mono,monospace;font-size:10px;padding:1px 4px;border:1px solid #e2e4e7;border-radius:3px;color:#7a7a7a}.c-divider{width:1px;height:16px;background:#e2e4e7;margin:0 2px}.c-token{display:inline-flex;align-items:center;gap:4px;padding:0 0 0 12px;border:1px solid var(--sd-primary-light);border-radius:999px;background:var(--sd-primary-light);font-size:13px;color:#5f6368;white-space:nowrap;overflow:hidden}.c-density-compact .c-token{height:28px}.c-density-comfortable .c-token{height:32px}.c-token-icon{font-size:14px;width:14px;height:14px;line-height:14px;color:inherit}.c-token-field{font-weight:500;line-height:1}.c-token-op{display:inline-flex;align-items:center;color:inherit}.c-token-op ::ng-deep .c-op-trigger{padding:0;color:inherit}.c-token-op ::ng-deep .c-op-icon svg{width:14px;height:14px;display:block}.c-token-value-edit sd-select,.c-token-value-edit sd-date,.c-token-value-edit sd-datetime{display:inline-flex;align-items:center;line-height:1}.c-token ::ng-deep .mat-mdc-form-field{line-height:1}.c-token ::ng-deep .mat-mdc-text-field-wrapper,.c-token ::ng-deep .mat-mdc-form-field-flex{padding:0;min-height:0;background:transparent}.c-token ::ng-deep .mat-mdc-form-field-infix{padding:0;min-height:0;width:auto;border:0;display:inline-flex;align-items:center}.c-token ::ng-deep .mat-mdc-select-value,.c-token ::ng-deep .mat-mdc-input-element,.c-token ::ng-deep input{line-height:1;font-size:13px;padding:0;height:auto;color:inherit}.c-token ::ng-deep .mat-mdc-select-value{display:inline-flex;align-items:center;min-width:0;max-width:200px;overflow:hidden;text-overflow:ellipsis;color:var(--sd-primary);font-weight:500}.c-token ::ng-deep .sd-suffix-icon{display:none}.c-token-sep{color:#7a7a7a}.c-token-dash{color:#7a7a7a;padding:0 4px;line-height:1}.c-token-value{border:none;background:transparent;padding:0;font:inherit;font-weight:500;color:var(--sd-primary);cursor:pointer}.c-token-value:hover{text-decoration:underline}.c-token-value-edit{display:inline-flex;align-items:center;gap:4px;max-width:240px}.c-token-value-edit sd-input,.c-token-value-edit sd-input-number{display:inline-flex;max-width:140px}.c-token-value-edit sd-date-range{display:inline-flex;max-width:220px;min-width:0;flex:0 1 auto}.c-token-multi-head{display:inline-block;max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom}.c-token-remove{align-self:stretch;display:inline-flex;align-items:center;justify-content:center;border:none;border-left:1px solid rgba(0,0,0,.04);border-radius:0;background:transparent;cursor:pointer;color:inherit}.c-density-compact .c-token-remove{width:26px}.c-density-comfortable .c-token-remove{width:28px}.c-token-remove sd-icon{font-size:14px;width:14px;height:14px}.c-token-remove:hover{background:#0000000d}.c-add-filter{display:inline-flex;align-items:center;justify-content:center;padding:0;border-radius:999px;border:1px dashed #d6d8db;background:transparent;cursor:pointer;color:#5f6368;transition:border-color .12s ease,color .12s ease,background-color .12s ease}.c-density-compact .c-add-filter{width:28px;height:28px}.c-density-comfortable .c-add-filter{width:32px;height:32px}.c-add-filter sd-icon{font-size:18px;width:18px;height:18px}.c-add-filter:hover:not([disabled]){border-color:var(--sd-primary);color:var(--sd-primary)}.c-add-filter[disabled]{opacity:.5;cursor:not-allowed}.c-or-connector{font-family:Roboto Mono,monospace;font-size:10px;font-weight:700;letter-spacing:.6px;color:#7a7a7a;padding:0 2px}::ng-deep .mat-mdc-menu-panel.c-chip-popover{min-width:280px;max-width:320px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header{display:flex;align-items:center;gap:8px;padding:10px 12px;border-bottom:1px solid #e2e4e7;font-size:13px;font-weight:500;color:#212121}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header sd-icon{font-size:16px;width:16px;height:16px;color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header .c-pop-header-operator{margin-left:auto}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field{display:inline-flex;align-items:center;gap:4px;padding:2px 4px 2px 6px;margin-left:-4px;background:transparent;border:1px solid transparent;border-radius:4px;font:inherit;font-weight:500;color:#212121;cursor:pointer;transition:background-color .12s ease,border-color .12s ease}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field sd-icon{font-size:14px!important;width:14px!important;height:14px!important;color:#7a7a7a!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field:hover{background:#f5f6f7;border-color:#e2e4e7}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-body{padding:10px 12px;display:flex;flex-direction:column;gap:12px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-input{width:100%;height:32px;padding:0 10px;border:1px solid #d6d8db;border-radius:6px;background:#fff;font-family:inherit;font-size:13px;color:#212121;outline:none}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-input:focus{border-color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-between{display:grid;grid-template-columns:1fr 1fr;gap:8px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-boolean{display:flex;gap:8px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-boolean button{flex:1;height:32px;border:1px solid #d6d8db;border-radius:6px;background:#fff;font:inherit;font-size:13px;cursor:pointer}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-boolean button.c-pop-boolean-active{background:var(--sd-primary-light);border-color:var(--sd-primary);color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values{max-height:220px;overflow-y:auto;margin:0;padding:0;list-style:none}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values li{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:4px;font-size:13px;cursor:pointer;-webkit-user-select:none;user-select:none}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values li:hover{background:#f5f6f7}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values li.c-pop-value-selected{background:var(--sd-primary-light);color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values li input[type=checkbox],::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values li input[type=radio]{accent-color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values-loading,::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values-empty{padding:10px 8px;font-size:12px;color:#7a7a7a}.c-bool-toggle{display:inline-flex;align-items:center;gap:4px}.c-bool-toggle ::ng-deep .mdc-button,.c-bool-toggle ::ng-deep button[mat-button],.c-bool-toggle ::ng-deep button[mat-stroked-button],.c-bool-toggle ::ng-deep button[mat-flat-button]{border-radius:999px;min-width:0;padding:0 12px}\n", ".c-query-bar ::ng-deep .mat-mdc-form-field .mdc-notched-outline__leading,.c-query-bar ::ng-deep .mat-mdc-form-field .mdc-notched-outline__notch,.c-query-bar ::ng-deep .mat-mdc-form-field .mdc-notched-outline__trailing{border-color:#d6d8db!important;transition:border-color .12s ease}.c-query-bar ::ng-deep .mat-mdc-form-field .mdc-notched-outline__leading{border-radius:6px 0 0 6px!important}.c-query-bar ::ng-deep .mat-mdc-form-field .mdc-notched-outline__trailing{border-radius:0 6px 6px 0!important}.c-query-bar ::ng-deep .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__leading,.c-query-bar ::ng-deep .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__notch,.c-query-bar ::ng-deep .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__trailing{border-color:#b9bdc3!important}.c-query-bar ::ng-deep .mat-mdc-form-field.mat-focused .mdc-notched-outline__leading,.c-query-bar ::ng-deep .mat-mdc-form-field.mat-focused .mdc-notched-outline__notch,.c-query-bar ::ng-deep .mat-mdc-form-field.mat-focused .mdc-notched-outline__trailing{border-color:var(--sd-primary)!important;border-width:1px!important}.c-query-bar ::ng-deep .mat-mdc-form-field .mat-mdc-select-arrow,.c-query-bar ::ng-deep .mat-mdc-form-field .mat-mdc-form-field-icon-suffix sd-icon{color:#5f6368}.c-query-bar ::ng-deep .mat-mdc-form-field.mat-focused .mat-mdc-select-arrow{color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mdc-notched-outline__trailing,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mdc-notched-outline__trailing{border-color:#d6d8db!important;transition:border-color .12s ease}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mdc-notched-outline__leading{border-radius:6px 0 0 6px!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mdc-notched-outline__trailing,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mdc-notched-outline__trailing{border-radius:0 6px 6px 0!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__trailing,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__trailing{border-color:#b9bdc3!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field.mat-focused .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field.mat-focused .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field.mat-focused .mdc-notched-outline__trailing,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field.mat-focused .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field.mat-focused .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field.mat-focused .mdc-notched-outline__trailing{border-color:var(--sd-primary)!important;border-width:1px!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mat-mdc-select-arrow,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mat-mdc-form-field-icon-suffix sd-icon,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mat-mdc-select-arrow,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mat-mdc-form-field-icon-suffix sd-icon{color:#5f6368}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field.mat-focused .mat-mdc-select-arrow,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field.mat-focused .mat-mdc-select-arrow{color:var(--sd-primary)}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.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: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "directive", type: i1.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: SdQueryActionsBar, selector: "sd-query-actions-bar", inputs: ["showLogicToggle", "logic", "filtersCount", "showSavedFilters", "savedFiltersKey", "query", "showClearAll", "canSearch"], outputs: ["logicChange", "applyFilter", "clear", "search"] }, { kind: "component", type: SdQueryBuildChip, selector: "sd-query-build-chip", inputs: ["building", "density", "allowedOperators", "multiple", "showOperator", "autoId"], outputs: ["pickOperator", "commitValue", "cancel", "seamlessCommit", "draftChange", "draftCommit"] }, { kind: "component", type: SdQueryChipPopover, selector: "sd-query-chip-popover", inputs: ["field", "filter", "chipIndex", "autoIdBase", "switchPickerMenu"], outputs: ["commit", "swapField"] }, { kind: "component", type: SdQueryFieldPicker, selector: "sd-query-field-picker", inputs: ["fields", "currentKey", "usedKeys"], outputs: ["pick"] }, { kind: "component", type: SdQueryInlineChip, selector: "sd-query-inline-chip", inputs: ["field", "filter", "density", "multiple", "showOperator", "autoId", "valueText", "isNoData"], outputs: ["commit", "commitRange", "liveChange", "remove"] }, { kind: "component", type: SdQueryInlineValueChip, selector: "sd-query-inline-value-chip", inputs: ["field", "operator", "value", "density", "showOperator", "autoId", "autofocus"], outputs: ["valueChange", "remove"] }, { kind: "component", type: SdQueryPopoverChip, selector: "sd-query-popover-chip", inputs: ["field", "filter", "active", "showOperator", "valueText", "menu"], outputs: ["open", "remove"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1520
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SdQueryBar, isStandalone: true, selector: "sd-query-bar", inputs: { option: { classPropertyName: "option", publicName: "option", isSignal: true, isRequired: false, transformFunction: null }, autoIdInput: { classPropertyName: "autoIdInput", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, fields: { classPropertyName: "fields", publicName: "fields", isSignal: true, isRequired: false, transformFunction: null }, filters: { classPropertyName: "filters", publicName: "filters", isSignal: true, isRequired: false, transformFunction: null }, logic: { classPropertyName: "logic", publicName: "logic", isSignal: true, isRequired: false, transformFunction: null }, search: { classPropertyName: "search", publicName: "search", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, density: { classPropertyName: "density", publicName: "density", isSignal: true, isRequired: false, transformFunction: null }, showSearch: { classPropertyName: "showSearch", publicName: "showSearch", isSignal: true, isRequired: false, transformFunction: null }, showSavedFilters: { classPropertyName: "showSavedFilters", publicName: "showSavedFilters", isSignal: true, isRequired: false, transformFunction: null }, savedFiltersKey: { classPropertyName: "savedFiltersKey", publicName: "savedFiltersKey", isSignal: true, isRequired: false, transformFunction: null }, showLogicToggle: { classPropertyName: "showLogicToggle", publicName: "showLogicToggle", isSignal: true, isRequired: false, transformFunction: null }, showClearAll: { classPropertyName: "showClearAll", publicName: "showClearAll", isSignal: true, isRequired: false, transformFunction: null }, showOperatorOnChip: { classPropertyName: "showOperatorOnChip", publicName: "showOperatorOnChip", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { filters: "filtersChange", logic: "logicChange", search: "searchChange", queryChange: "queryChange", apply: "apply" }, viewQueries: [{ propertyName: "popoverChips", predicate: SdQueryPopoverChip, descendants: true, isSignal: true }, { propertyName: "buildChip", first: true, predicate: SdQueryBuildChip, descendants: true, isSignal: true }, { propertyName: "chipPopover", first: true, predicate: SdQueryChipPopover, descendants: true, isSignal: true }], ngImport: i0, template: "<!--\n sd-query-bar \u2014 unified filter chip row (Jira/Linear/Notion style).\n Visual spec: refs/design_handoff_sd_query_bar/README.md\n TODO(next-iter): replace inline placeholders for field-picker + chip-popover\n with extracted sub-components driven by CDK overlay.\n-->\n@let _filters = filters();\n@let _resolvedFields = resolvedFields();\n@let _resolvedDensity = resolvedDensity();\n@let _showOrConnector = showOrConnector();\n@let _usedFieldKeys = usedFieldKeys();\n@let _editingIndex = editingIndex();\n@let _resolvedMode = resolvedMode();\n@let _resolvedShowOperatorOnChip = resolvedShowOperatorOnChip();\n@let _noFieldsLabel = noFieldsLabel();\n@let _addFilterLabel = addFilterLabel();\n\n<div\n class=\"c-query-bar\"\n [class.c-density-compact]=\"_resolvedDensity === 'compact'\"\n [class.c-density-comfortable]=\"_resolvedDensity === 'comfortable'\">\n\n <!-- Main region \u2014 search + filters + add-filter. Wraps internally; the right\n action toolbar stays anchored and does NOT flow with this. -->\n <div class=\"c-query-bar__main\">\n\n <!-- Free-text search (left, optional) -->\n @if (resolvedShowSearch()) {\n <div class=\"c-search\">\n <sd-icon class=\"c-search-icon\" name=\"search\"></sd-icon>\n <input\n class=\"c-search-input\"\n type=\"text\"\n [ngModel]=\"search()\"\n (ngModelChange)=\"setSearch($event)\"\n (keydown.enter)=\"triggerApply()\"\n [placeholder]=\"searchPlaceholder()\" />\n </div>\n <span class=\"c-divider\" aria-hidden=\"true\"></span>\n }\n\n <!-- Chip popover \u2014 extracted into <sd-query-chip-popover>. Declared BEFORE chips\n so the popover-mode @for chips can bind their [menu] to `chipPopover.menu()`.\n why: m\u1EDF popover \u0111\u1EC3 EDIT ch\u1EC9 cho ch\u1EC9nh operator + value \u2014 KH\u00D4NG \u0111\u1ED5i field.\n B\u1ECF [switchPickerMenu] \u2192 header render nh\u00E3n field t\u0129nh (read-only). Mu\u1ED1n \u0111\u1ED5i\n field th\u00EC xo\u00E1 chip r\u1ED3i th\u00EAm l\u1EA1i. -->\n <sd-query-chip-popover\n #chipPopover\n [field]=\"editingField() ?? undefined\"\n [filter]=\"_editingIndex !== null ? $any(_filters[_editingIndex!]) : undefined\"\n [chipIndex]=\"_editingIndex\"\n [autoIdBase]=\"resolvedAutoId() ?? 'qb'\"\n (commit)=\"onChipPopoverCommit($event)\" />\n\n @if (_resolvedMode === 'inline') {\n <!-- ===== INLINE MODE (GitLab-style token builder) ===== -->\n\n <!-- Completed chips.\n why: track theo rowId(filter) ch\u1EE9 KH\u00D4NG theo $index \u2014 track $index g\u1EAFn chip v\u1EDBi v\u1ECB tr\u00ED,\n n\u00EAn xo\u00E1 filter \u0111\u1EA7u ti\u00EAn s\u1EBD bind l\u1EA1i chip c\u0169 sang filter k\u1EBF ti\u1EBFp v\u00E0 k\u00E9o theo state n\u1ED9i b\u1ED9\n (inline-chip `#editing`, inline-value-chip `draft`) sang sai filter. -->\n @for (filter of _filters; track rowId(filter); let i = $index) {\n @if (_showOrConnector && i > 0) {\n <span class=\"c-or-connector\" aria-hidden=\"true\">OR</span>\n }\n @let _field = fieldByKey()[$any(filter).field];\n @if (_field) {\n @let _op = $any(filter.operator);\n @let _data = $any(filter).data;\n @if (_field.type === 'string' || _field.type === 'number') {\n <!-- Seamless chip \u2014 the pill is the input (string / number). -->\n <sd-query-inline-value-chip\n [field]=\"_field\"\n [operator]=\"_op\"\n [value]=\"_data\"\n [density]=\"_resolvedDensity\"\n [showOperator]=\"_resolvedShowOperatorOnChip\"\n [autoId]=\"inlineAutoId(i, 'value')\"\n (valueChange)=\"updateFilterData(i, $event)\"\n (remove)=\"removeFilter(i)\" />\n } @else {\n <sd-query-inline-chip\n [field]=\"_field\"\n [filter]=\"filter\"\n [density]=\"_resolvedDensity\"\n [multiple]=\"multiple(_op)\"\n [showOperator]=\"_resolvedShowOperatorOnChip\"\n [autoId]=\"inlineAutoId(i, 'value')\"\n [valueText]=\"chipValueText(filter)\"\n [isNoData]=\"isNoDataOperator(_op)\"\n (commit)=\"updateFilterData(i, $event)\"\n (commitRange)=\"setFilterRange(i, $event)\"\n (liveChange)=\"updateFilterData(i, $event)\"\n (remove)=\"removeFilter(i)\" />\n }\n }\n }\n\n <!-- Building chip (in-progress) \u2014 extracted into <sd-query-build-chip>. -->\n @if (building(); as _b) {\n <sd-query-build-chip\n [building]=\"_b\"\n [density]=\"_resolvedDensity\"\n [allowedOperators]=\"allowedOperatorsFor(_b.field)\"\n [multiple]=\"multiple($any(_b.operator))\"\n [showOperator]=\"_resolvedShowOperatorOnChip\"\n (pickOperator)=\"pickBuildOperator($any($event))\"\n (commitValue)=\"commitBuildValue($event)\"\n (cancel)=\"cancelBuild()\"\n (seamlessCommit)=\"onBuildSeamlessCommit($event)\" />\n }\n\n <!-- Add filter -->\n <button\n type=\"button\"\n class=\"c-add-filter\"\n [matMenuTriggerFor]=\"addPicker.menu()\"\n [disabled]=\"_resolvedFields.length === 0 || _usedFieldKeys.size >= _resolvedFields.length\"\n [matTooltip]=\"_resolvedFields.length === 0 ? _noFieldsLabel : _addFilterLabel\">\n <sd-icon name=\"add\"></sd-icon>\n </button>\n } @else {\n <!-- ===== POPOVER MODE (default) \u2014 compact chips edited via mat-menu ===== -->\n <!-- why: track theo rowId(filter) \u2014 xem ghi ch\u00FA \u1EDF nh\u00E1nh inline. -->\n @for (filter of _filters; track rowId(filter); let i = $index) {\n @if (_showOrConnector && i > 0) {\n <span class=\"c-or-connector\" aria-hidden=\"true\">OR</span>\n }\n @let _field = fieldByKey()[$any(filter).field];\n @let _active = isFilterActive(filter);\n <!-- why: menu close commit is handled by chipPopover's (commit) \u2192\n onChipPopoverCommit; popover-chip only signals open + remove here. -->\n <sd-query-popover-chip\n [field]=\"_field\"\n [filter]=\"filter\"\n [active]=\"_active\"\n [showOperator]=\"_resolvedShowOperatorOnChip\"\n [valueText]=\"chipValueText(filter)\"\n [menu]=\"chipPopover.menu()!\"\n (open)=\"openChipPopover(i)\"\n (remove)=\"removeFilter(i)\" />\n }\n\n <!-- Add filter trigger -->\n <button\n type=\"button\"\n class=\"c-add-filter\"\n [matMenuTriggerFor]=\"addPicker.menu()\"\n [disabled]=\"_resolvedFields.length === 0 || _usedFieldKeys.size >= _resolvedFields.length\"\n [matTooltip]=\"_resolvedFields.length === 0 ? _noFieldsLabel : _addFilterLabel\">\n <sd-icon name=\"add\"></sd-icon>\n </button>\n }\n\n </div><!-- /.c-query-bar__main -->\n\n <!-- Field picker \u2014 list available fields, fade ones already in use. -->\n <sd-query-field-picker\n #addPicker\n [fields]=\"_resolvedFields\"\n [usedKeys]=\"_usedFieldKeys\"\n (pick)=\"_resolvedMode === 'inline' ? beginBuild($event) : addFilter($event)\" />\n\n <sd-query-actions-bar\n [showLogicToggle]=\"resolvedShowLogicToggle()\"\n [(logic)]=\"logic\"\n [filtersCount]=\"_filters.length\"\n [showSavedFilters]=\"resolvedShowSavedFilters()\"\n [savedFiltersKey]=\"resolvedSavedFiltersKey()\"\n [query]=\"currentQuery()\"\n (applyFilter)=\"onApplyFilter($event)\"\n [showClearAll]=\"resolvedShowClearAll()\"\n [canSearch]=\"canSearch()\"\n (clear)=\"clearAll()\"\n (search)=\"triggerApply()\" />\n</div>\n", styles: [":host{display:block;font-family:Roboto,system-ui,sans-serif}.c-query-bar{display:flex;align-items:flex-start;flex-wrap:nowrap;gap:8px;background:#fff;border:1px solid #e2e4e7;border-radius:8px;box-shadow:0 1px 2px #0f172a0a}.c-query-bar.c-density-compact{padding:8px}.c-query-bar.c-density-comfortable{padding:10px}.c-query-bar__main{flex:1 1 auto;min-width:0;display:flex;align-items:center;flex-wrap:wrap;gap:6px}.c-search{display:flex;align-items:center;gap:6px;min-width:220px;padding:0 10px;border:1px solid #d6d8db;border-radius:6px;background:#fff}.c-density-compact .c-search{height:28px}.c-density-comfortable .c-search{height:32px}.c-search .c-search-icon{font-size:16px;width:16px;height:16px;color:#7a7a7a}.c-search .c-search-input{flex:1;border:none;outline:none;background:transparent;font-size:13px;color:#212121}.c-search .c-search-input::placeholder{color:#7a7a7a}.c-search .c-search-input:focus-visible{outline:2px solid var(--sd-primary);outline-offset:1px;border-radius:4px}.c-search .c-search-kbd{font-family:Roboto Mono,monospace;font-size:10px;padding:1px 4px;border:1px solid #e2e4e7;border-radius:3px;color:#7a7a7a}.c-divider{width:1px;height:16px;background:#e2e4e7;margin:0 2px}.c-token{display:inline-flex;align-items:center;gap:4px;padding:0 0 0 12px;border:1px solid var(--sd-primary-light);border-radius:999px;background:var(--sd-primary-light);font-size:13px;color:#5f6368;white-space:nowrap;overflow:hidden}.c-density-compact .c-token{height:28px}.c-density-comfortable .c-token{height:32px}.c-token-icon{font-size:14px;width:14px;height:14px;line-height:14px;color:inherit}.c-token-field{font-weight:500;line-height:1}.c-token-op{display:inline-flex;align-items:center;color:inherit}.c-token-op ::ng-deep .c-op-trigger{padding:0;color:inherit}.c-token-op ::ng-deep .c-op-icon svg{width:14px;height:14px;display:block}.c-token-value-edit sd-select,.c-token-value-edit sd-date,.c-token-value-edit sd-datetime{display:inline-flex;align-items:center;line-height:1}.c-token ::ng-deep .mat-mdc-form-field{line-height:1}.c-token ::ng-deep .mat-mdc-text-field-wrapper,.c-token ::ng-deep .mat-mdc-form-field-flex{padding:0;min-height:0;background:transparent}.c-token ::ng-deep .mat-mdc-form-field-infix{padding:0;min-height:0;width:auto;border:0;display:inline-flex;align-items:center}.c-token ::ng-deep .mat-mdc-select-value,.c-token ::ng-deep .mat-mdc-input-element,.c-token ::ng-deep input{line-height:1;font-size:13px;padding:0;height:auto;color:inherit}.c-token ::ng-deep .mat-mdc-select-value{display:inline-flex;align-items:center;min-width:0;max-width:200px;overflow:hidden;text-overflow:ellipsis;color:var(--sd-primary);font-weight:500}.c-token ::ng-deep .sd-suffix-icon{display:none}.c-token-sep{color:#7a7a7a}.c-token-dash{color:#7a7a7a;padding:0 4px;line-height:1}.c-token-value{border:none;background:transparent;padding:0;font:inherit;font-weight:500;color:var(--sd-primary);cursor:pointer}.c-token-value:hover{text-decoration:underline}.c-token-value-edit{display:inline-flex;align-items:center;gap:4px;max-width:240px}.c-token-value-edit sd-input,.c-token-value-edit sd-input-number{display:inline-flex;max-width:140px}.c-token-value-edit sd-date-range{display:inline-flex;max-width:220px;min-width:0;flex:0 1 auto}.c-token-multi-head{display:inline-block;max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom}.c-token-remove{align-self:stretch;display:inline-flex;align-items:center;justify-content:center;border:none;border-left:1px solid rgba(0,0,0,.04);border-radius:0;background:transparent;cursor:pointer;color:inherit}.c-density-compact .c-token-remove{width:26px}.c-density-comfortable .c-token-remove{width:28px}.c-token-remove sd-icon{font-size:14px;width:14px;height:14px}.c-token-remove:hover{background:#0000000d}.c-add-filter{display:inline-flex;align-items:center;justify-content:center;padding:0;border-radius:999px;border:1px dashed #d6d8db;background:transparent;cursor:pointer;color:#5f6368;transition:border-color .12s ease,color .12s ease,background-color .12s ease}.c-density-compact .c-add-filter{width:28px;height:28px}.c-density-comfortable .c-add-filter{width:32px;height:32px}.c-add-filter sd-icon{font-size:18px;width:18px;height:18px}.c-add-filter:hover:not([disabled]){border-color:var(--sd-primary);color:var(--sd-primary)}.c-add-filter[disabled]{opacity:.5;cursor:not-allowed}.c-or-connector{font-family:Roboto Mono,monospace;font-size:10px;font-weight:700;letter-spacing:.6px;color:#7a7a7a;padding:0 2px}::ng-deep .mat-mdc-menu-panel.c-chip-popover{min-width:280px;max-width:320px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header{display:flex;align-items:center;gap:8px;padding:10px 12px;border-bottom:1px solid #e2e4e7;font-size:13px;font-weight:500;color:#212121}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header sd-icon{font-size:16px;width:16px;height:16px;color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header .c-pop-header-operator{margin-left:auto}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field{display:inline-flex;align-items:center;gap:4px;padding:2px 4px 2px 6px;margin-left:-4px;background:transparent;border:1px solid transparent;border-radius:4px;font:inherit;font-weight:500;color:#212121;cursor:pointer;transition:background-color .12s ease,border-color .12s ease}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field sd-icon{font-size:14px!important;width:14px!important;height:14px!important;color:#7a7a7a!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field:hover{background:#f5f6f7;border-color:#e2e4e7}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-body{padding:10px 12px;display:flex;flex-direction:column;gap:12px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-input{width:100%;height:32px;padding:0 10px;border:1px solid #d6d8db;border-radius:6px;background:#fff;font-family:inherit;font-size:13px;color:#212121;outline:none}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-input:focus{border-color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-input:focus-visible{outline:2px solid var(--sd-primary);outline-offset:1px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-between{display:grid;grid-template-columns:1fr 1fr;gap:8px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-boolean{display:flex;gap:8px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-boolean button{flex:1;height:32px;border:1px solid #d6d8db;border-radius:6px;background:#fff;font:inherit;font-size:13px;cursor:pointer}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-boolean button.c-pop-boolean-active{background:var(--sd-primary-light);border-color:var(--sd-primary);color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values{max-height:220px;overflow-y:auto;margin:0;padding:0;list-style:none}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values li{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:4px;font-size:13px;cursor:pointer;-webkit-user-select:none;user-select:none}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values li:hover{background:#f5f6f7}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values li.c-pop-value-selected{background:var(--sd-primary-light);color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values li input[type=checkbox],::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values li input[type=radio]{accent-color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values-loading,::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values-empty{padding:10px 8px;font-size:12px;color:#7a7a7a}.c-bool-toggle{display:inline-flex;align-items:center;gap:4px}.c-bool-toggle ::ng-deep .mdc-button,.c-bool-toggle ::ng-deep button[mat-button],.c-bool-toggle ::ng-deep button[mat-stroked-button],.c-bool-toggle ::ng-deep button[mat-flat-button]{border-radius:999px;min-width:0;padding:0 12px}\n", ".c-query-bar ::ng-deep .mat-mdc-form-field .mdc-notched-outline__leading,.c-query-bar ::ng-deep .mat-mdc-form-field .mdc-notched-outline__notch,.c-query-bar ::ng-deep .mat-mdc-form-field .mdc-notched-outline__trailing{border-color:#d6d8db!important;transition:border-color .12s ease}.c-query-bar ::ng-deep .mat-mdc-form-field .mdc-notched-outline__leading{border-radius:6px 0 0 6px!important}.c-query-bar ::ng-deep .mat-mdc-form-field .mdc-notched-outline__trailing{border-radius:0 6px 6px 0!important}.c-query-bar ::ng-deep .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__leading,.c-query-bar ::ng-deep .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__notch,.c-query-bar ::ng-deep .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__trailing{border-color:#b9bdc3!important}.c-query-bar ::ng-deep .mat-mdc-form-field.mat-focused .mdc-notched-outline__leading,.c-query-bar ::ng-deep .mat-mdc-form-field.mat-focused .mdc-notched-outline__notch,.c-query-bar ::ng-deep .mat-mdc-form-field.mat-focused .mdc-notched-outline__trailing{border-color:var(--sd-primary)!important;border-width:1px!important}.c-query-bar ::ng-deep .mat-mdc-form-field .mat-mdc-select-arrow,.c-query-bar ::ng-deep .mat-mdc-form-field .mat-mdc-form-field-icon-suffix sd-icon{color:#5f6368}.c-query-bar ::ng-deep .mat-mdc-form-field.mat-focused .mat-mdc-select-arrow{color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mdc-notched-outline__trailing,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mdc-notched-outline__trailing{border-color:#d6d8db!important;transition:border-color .12s ease}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mdc-notched-outline__leading{border-radius:6px 0 0 6px!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mdc-notched-outline__trailing,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mdc-notched-outline__trailing{border-radius:0 6px 6px 0!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__trailing,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__trailing{border-color:#b9bdc3!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field.mat-focused .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field.mat-focused .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field.mat-focused .mdc-notched-outline__trailing,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field.mat-focused .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field.mat-focused .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field.mat-focused .mdc-notched-outline__trailing{border-color:var(--sd-primary)!important;border-width:1px!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mat-mdc-select-arrow,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mat-mdc-form-field-icon-suffix sd-icon,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mat-mdc-select-arrow,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mat-mdc-form-field-icon-suffix sd-icon{color:#5f6368}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field.mat-focused .mat-mdc-select-arrow,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field.mat-focused .mat-mdc-select-arrow{color:var(--sd-primary)}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.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: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "directive", type: i1.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: SdQueryActionsBar, selector: "sd-query-actions-bar", inputs: ["showLogicToggle", "logic", "filtersCount", "showSavedFilters", "savedFiltersKey", "query", "showClearAll", "canSearch"], outputs: ["logicChange", "applyFilter", "clear", "search"] }, { kind: "component", type: SdQueryBuildChip, selector: "sd-query-build-chip", inputs: ["building", "density", "allowedOperators", "multiple", "showOperator", "autoId"], outputs: ["pickOperator", "commitValue", "cancel", "seamlessCommit", "draftChange", "draftCommit"] }, { kind: "component", type: SdQueryChipPopover, selector: "sd-query-chip-popover", inputs: ["field", "filter", "chipIndex", "autoIdBase", "switchPickerMenu"], outputs: ["commit", "swapField"] }, { kind: "component", type: SdQueryFieldPicker, selector: "sd-query-field-picker", inputs: ["fields", "currentKey", "usedKeys"], outputs: ["pick"] }, { kind: "component", type: SdQueryInlineChip, selector: "sd-query-inline-chip", inputs: ["field", "filter", "density", "multiple", "showOperator", "autoId", "valueText", "isNoData"], outputs: ["commit", "commitRange", "liveChange", "remove"] }, { kind: "component", type: SdQueryInlineValueChip, selector: "sd-query-inline-value-chip", inputs: ["field", "operator", "value", "density", "showOperator", "autoId", "autofocus"], outputs: ["valueChange", "remove"] }, { kind: "component", type: SdQueryPopoverChip, selector: "sd-query-popover-chip", inputs: ["field", "filter", "active", "showOperator", "valueText", "menu"], outputs: ["open", "remove"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1359
1521
  }
1360
1522
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdQueryBar, decorators: [{
1361
1523
  type: Component,
@@ -1372,7 +1534,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
1372
1534
  SdQueryInlineChip,
1373
1535
  SdQueryInlineValueChip,
1374
1536
  SdQueryPopoverChip,
1375
- ], template: "<!--\n sd-query-bar \u2014 unified filter chip row (Jira/Linear/Notion style).\n Visual spec: refs/design_handoff_sd_query_bar/README.md\n TODO(next-iter): replace inline placeholders for field-picker + chip-popover\n with extracted sub-components driven by CDK overlay.\n-->\n@let _filters = filters();\n@let _resolvedFields = resolvedFields();\n@let _resolvedDensity = resolvedDensity();\n@let _showOrConnector = showOrConnector();\n@let _usedFieldKeys = usedFieldKeys();\n@let _editingIndex = editingIndex();\n@let _resolvedMode = resolvedMode();\n@let _resolvedShowOperatorOnChip = resolvedShowOperatorOnChip();\n\n<div\n class=\"c-query-bar\"\n [class.c-density-compact]=\"_resolvedDensity === 'compact'\"\n [class.c-density-comfortable]=\"_resolvedDensity === 'comfortable'\">\n\n <!-- Main region \u2014 search + filters + add-filter. Wraps internally; the right\n action toolbar stays anchored and does NOT flow with this. -->\n <div class=\"c-query-bar__main\">\n\n <!-- Free-text search (left, optional) -->\n @if (resolvedShowSearch()) {\n <div class=\"c-search\">\n <sd-icon class=\"c-search-icon\" name=\"search\"></sd-icon>\n <input\n class=\"c-search-input\"\n type=\"text\"\n [ngModel]=\"search()\"\n (ngModelChange)=\"setSearch($event)\"\n (keydown.enter)=\"triggerApply()\"\n placeholder=\"T\u00ECm ki\u1EBFm...\" />\n </div>\n <span class=\"c-divider\" aria-hidden=\"true\"></span>\n }\n\n <!-- Chip popover \u2014 extracted into <sd-query-chip-popover>. Declared BEFORE chips\n so the popover-mode @for chips can bind their [menu] to `chipPopover.menu()`.\n why: m\u1EDF popover \u0111\u1EC3 EDIT ch\u1EC9 cho ch\u1EC9nh operator + value \u2014 KH\u00D4NG \u0111\u1ED5i field.\n B\u1ECF [switchPickerMenu] \u2192 header render nh\u00E3n field t\u0129nh (read-only). Mu\u1ED1n \u0111\u1ED5i\n field th\u00EC xo\u00E1 chip r\u1ED3i th\u00EAm l\u1EA1i. -->\n <sd-query-chip-popover\n #chipPopover\n [field]=\"editingField() ?? undefined\"\n [filter]=\"_editingIndex !== null ? $any(_filters[_editingIndex!]) : undefined\"\n [chipIndex]=\"_editingIndex\"\n [autoIdBase]=\"resolvedAutoId() ?? 'qb'\"\n (commit)=\"onChipPopoverCommit($event)\" />\n\n @if (_resolvedMode === 'inline') {\n <!-- ===== INLINE MODE (GitLab-style token builder) ===== -->\n\n <!-- Completed chips -->\n @for (filter of _filters; track $index; let i = $index) {\n @if (_showOrConnector && i > 0) {\n <span class=\"c-or-connector\" aria-hidden=\"true\">OR</span>\n }\n @let _field = fieldByKey()[$any(filter).field];\n @if (_field) {\n @let _op = $any(filter.operator);\n @let _data = $any(filter).data;\n @if (_field.type === 'string' || _field.type === 'number') {\n <!-- Seamless chip \u2014 the pill is the input (string / number). -->\n <sd-query-inline-value-chip\n [field]=\"_field\"\n [operator]=\"_op\"\n [value]=\"_data\"\n [density]=\"_resolvedDensity\"\n [showOperator]=\"_resolvedShowOperatorOnChip\"\n [autoId]=\"inlineAutoId(i, 'value')\"\n (valueChange)=\"updateFilterData(i, $event)\"\n (remove)=\"removeFilter(i)\" />\n } @else {\n <sd-query-inline-chip\n [field]=\"_field\"\n [filter]=\"filter\"\n [density]=\"_resolvedDensity\"\n [multiple]=\"multiple(_op)\"\n [showOperator]=\"_resolvedShowOperatorOnChip\"\n [autoId]=\"inlineAutoId(i, 'value')\"\n [valueText]=\"chipValueText(filter)\"\n [isNoData]=\"isNoDataOperator(_op)\"\n (commit)=\"updateFilterData(i, $event)\"\n (commitRange)=\"setFilterRange(i, $event)\"\n (liveChange)=\"updateFilterData(i, $event)\"\n (remove)=\"removeFilter(i)\" />\n }\n }\n }\n\n <!-- Building chip (in-progress) \u2014 extracted into <sd-query-build-chip>. -->\n @if (building(); as _b) {\n <sd-query-build-chip\n [building]=\"_b\"\n [density]=\"_resolvedDensity\"\n [allowedOperators]=\"allowedOperatorsFor(_b.field)\"\n [multiple]=\"multiple($any(_b.operator))\"\n [showOperator]=\"_resolvedShowOperatorOnChip\"\n (pickOperator)=\"pickBuildOperator($any($event))\"\n (commitValue)=\"commitBuildValue($event)\"\n (cancel)=\"cancelBuild()\"\n (seamlessCommit)=\"onBuildSeamlessCommit($event)\" />\n }\n\n <!-- Add filter -->\n <button\n type=\"button\"\n class=\"c-add-filter\"\n [matMenuTriggerFor]=\"addPicker.menu()\"\n [disabled]=\"_resolvedFields.length === 0 || _usedFieldKeys.size >= _resolvedFields.length\"\n [matTooltip]=\"_resolvedFields.length === 0 ? 'Ch\u01B0a c\u1EA5u h\u00ECnh fields' : 'Th\u00EAm filter'\">\n <sd-icon name=\"add\"></sd-icon>\n </button>\n } @else {\n <!-- ===== POPOVER MODE (default) \u2014 compact chips edited via mat-menu ===== -->\n @for (filter of _filters; track $index; let i = $index) {\n @if (_showOrConnector && i > 0) {\n <span class=\"c-or-connector\" aria-hidden=\"true\">OR</span>\n }\n @let _field = fieldByKey()[$any(filter).field];\n @let _active = isFilterActive(filter);\n <!-- why: menu close commit is handled by chipPopover's (commit) \u2192\n onChipPopoverCommit; popover-chip only signals open + remove here. -->\n <sd-query-popover-chip\n [field]=\"_field\"\n [filter]=\"filter\"\n [active]=\"_active\"\n [showOperator]=\"_resolvedShowOperatorOnChip\"\n [valueText]=\"chipValueText(filter)\"\n [menu]=\"chipPopover.menu()!\"\n (open)=\"openChipPopover(i)\"\n (remove)=\"removeFilter(i)\" />\n }\n\n <!-- Add filter trigger -->\n <button\n type=\"button\"\n class=\"c-add-filter\"\n [matMenuTriggerFor]=\"addPicker.menu()\"\n [disabled]=\"_resolvedFields.length === 0 || _usedFieldKeys.size >= _resolvedFields.length\"\n [matTooltip]=\"_resolvedFields.length === 0 ? 'Ch\u01B0a c\u1EA5u h\u00ECnh fields' : 'Th\u00EAm filter'\">\n <sd-icon name=\"add\"></sd-icon>\n </button>\n }\n\n </div><!-- /.c-query-bar__main -->\n\n <!-- Field picker \u2014 list available fields, fade ones already in use. -->\n <sd-query-field-picker\n #addPicker\n [fields]=\"_resolvedFields\"\n [usedKeys]=\"_usedFieldKeys\"\n (pick)=\"_resolvedMode === 'inline' ? beginBuild($event) : addFilter($event)\" />\n\n <sd-query-actions-bar\n [showLogicToggle]=\"resolvedShowLogicToggle()\"\n [(logic)]=\"logic\"\n [filtersCount]=\"_filters.length\"\n [showSavedFilters]=\"resolvedShowSavedFilters()\"\n [savedFiltersKey]=\"resolvedSavedFiltersKey()\"\n [query]=\"currentQuery()\"\n (applyFilter)=\"onApplyFilter($event)\"\n [showClearAll]=\"resolvedShowClearAll()\"\n [canSearch]=\"canSearch()\"\n (clear)=\"clearAll()\"\n (search)=\"triggerApply()\" />\n</div>\n", styles: [":host{display:block;font-family:Roboto,system-ui,sans-serif}.c-query-bar{display:flex;align-items:flex-start;flex-wrap:nowrap;gap:8px;background:#fff;border:1px solid #e2e4e7;border-radius:8px;box-shadow:0 1px 2px #0f172a0a}.c-query-bar.c-density-compact{padding:8px}.c-query-bar.c-density-comfortable{padding:10px}.c-query-bar__main{flex:1 1 auto;min-width:0;display:flex;align-items:center;flex-wrap:wrap;gap:6px}.c-search{display:flex;align-items:center;gap:6px;min-width:220px;padding:0 10px;border:1px solid #d6d8db;border-radius:6px;background:#fff}.c-density-compact .c-search{height:28px}.c-density-comfortable .c-search{height:32px}.c-search .c-search-icon{font-size:16px;width:16px;height:16px;color:#7a7a7a}.c-search .c-search-input{flex:1;border:none;outline:none;background:transparent;font-size:13px;color:#212121}.c-search .c-search-input::placeholder{color:#7a7a7a}.c-search .c-search-kbd{font-family:Roboto Mono,monospace;font-size:10px;padding:1px 4px;border:1px solid #e2e4e7;border-radius:3px;color:#7a7a7a}.c-divider{width:1px;height:16px;background:#e2e4e7;margin:0 2px}.c-token{display:inline-flex;align-items:center;gap:4px;padding:0 0 0 12px;border:1px solid var(--sd-primary-light);border-radius:999px;background:var(--sd-primary-light);font-size:13px;color:#5f6368;white-space:nowrap;overflow:hidden}.c-density-compact .c-token{height:28px}.c-density-comfortable .c-token{height:32px}.c-token-icon{font-size:14px;width:14px;height:14px;line-height:14px;color:inherit}.c-token-field{font-weight:500;line-height:1}.c-token-op{display:inline-flex;align-items:center;color:inherit}.c-token-op ::ng-deep .c-op-trigger{padding:0;color:inherit}.c-token-op ::ng-deep .c-op-icon svg{width:14px;height:14px;display:block}.c-token-value-edit sd-select,.c-token-value-edit sd-date,.c-token-value-edit sd-datetime{display:inline-flex;align-items:center;line-height:1}.c-token ::ng-deep .mat-mdc-form-field{line-height:1}.c-token ::ng-deep .mat-mdc-text-field-wrapper,.c-token ::ng-deep .mat-mdc-form-field-flex{padding:0;min-height:0;background:transparent}.c-token ::ng-deep .mat-mdc-form-field-infix{padding:0;min-height:0;width:auto;border:0;display:inline-flex;align-items:center}.c-token ::ng-deep .mat-mdc-select-value,.c-token ::ng-deep .mat-mdc-input-element,.c-token ::ng-deep input{line-height:1;font-size:13px;padding:0;height:auto;color:inherit}.c-token ::ng-deep .mat-mdc-select-value{display:inline-flex;align-items:center;min-width:0;max-width:200px;overflow:hidden;text-overflow:ellipsis;color:var(--sd-primary);font-weight:500}.c-token ::ng-deep .sd-suffix-icon{display:none}.c-token-sep{color:#7a7a7a}.c-token-dash{color:#7a7a7a;padding:0 4px;line-height:1}.c-token-value{border:none;background:transparent;padding:0;font:inherit;font-weight:500;color:var(--sd-primary);cursor:pointer}.c-token-value:hover{text-decoration:underline}.c-token-value-edit{display:inline-flex;align-items:center;gap:4px;max-width:240px}.c-token-value-edit sd-input,.c-token-value-edit sd-input-number{display:inline-flex;max-width:140px}.c-token-value-edit sd-date-range{display:inline-flex;max-width:220px;min-width:0;flex:0 1 auto}.c-token-multi-head{display:inline-block;max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom}.c-token-remove{align-self:stretch;display:inline-flex;align-items:center;justify-content:center;border:none;border-left:1px solid rgba(0,0,0,.04);border-radius:0;background:transparent;cursor:pointer;color:inherit}.c-density-compact .c-token-remove{width:26px}.c-density-comfortable .c-token-remove{width:28px}.c-token-remove sd-icon{font-size:14px;width:14px;height:14px}.c-token-remove:hover{background:#0000000d}.c-add-filter{display:inline-flex;align-items:center;justify-content:center;padding:0;border-radius:999px;border:1px dashed #d6d8db;background:transparent;cursor:pointer;color:#5f6368;transition:border-color .12s ease,color .12s ease,background-color .12s ease}.c-density-compact .c-add-filter{width:28px;height:28px}.c-density-comfortable .c-add-filter{width:32px;height:32px}.c-add-filter sd-icon{font-size:18px;width:18px;height:18px}.c-add-filter:hover:not([disabled]){border-color:var(--sd-primary);color:var(--sd-primary)}.c-add-filter[disabled]{opacity:.5;cursor:not-allowed}.c-or-connector{font-family:Roboto Mono,monospace;font-size:10px;font-weight:700;letter-spacing:.6px;color:#7a7a7a;padding:0 2px}::ng-deep .mat-mdc-menu-panel.c-chip-popover{min-width:280px;max-width:320px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header{display:flex;align-items:center;gap:8px;padding:10px 12px;border-bottom:1px solid #e2e4e7;font-size:13px;font-weight:500;color:#212121}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header sd-icon{font-size:16px;width:16px;height:16px;color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header .c-pop-header-operator{margin-left:auto}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field{display:inline-flex;align-items:center;gap:4px;padding:2px 4px 2px 6px;margin-left:-4px;background:transparent;border:1px solid transparent;border-radius:4px;font:inherit;font-weight:500;color:#212121;cursor:pointer;transition:background-color .12s ease,border-color .12s ease}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field sd-icon{font-size:14px!important;width:14px!important;height:14px!important;color:#7a7a7a!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field:hover{background:#f5f6f7;border-color:#e2e4e7}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-body{padding:10px 12px;display:flex;flex-direction:column;gap:12px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-input{width:100%;height:32px;padding:0 10px;border:1px solid #d6d8db;border-radius:6px;background:#fff;font-family:inherit;font-size:13px;color:#212121;outline:none}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-input:focus{border-color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-between{display:grid;grid-template-columns:1fr 1fr;gap:8px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-boolean{display:flex;gap:8px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-boolean button{flex:1;height:32px;border:1px solid #d6d8db;border-radius:6px;background:#fff;font:inherit;font-size:13px;cursor:pointer}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-boolean button.c-pop-boolean-active{background:var(--sd-primary-light);border-color:var(--sd-primary);color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values{max-height:220px;overflow-y:auto;margin:0;padding:0;list-style:none}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values li{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:4px;font-size:13px;cursor:pointer;-webkit-user-select:none;user-select:none}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values li:hover{background:#f5f6f7}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values li.c-pop-value-selected{background:var(--sd-primary-light);color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values li input[type=checkbox],::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values li input[type=radio]{accent-color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values-loading,::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values-empty{padding:10px 8px;font-size:12px;color:#7a7a7a}.c-bool-toggle{display:inline-flex;align-items:center;gap:4px}.c-bool-toggle ::ng-deep .mdc-button,.c-bool-toggle ::ng-deep button[mat-button],.c-bool-toggle ::ng-deep button[mat-stroked-button],.c-bool-toggle ::ng-deep button[mat-flat-button]{border-radius:999px;min-width:0;padding:0 12px}\n", ".c-query-bar ::ng-deep .mat-mdc-form-field .mdc-notched-outline__leading,.c-query-bar ::ng-deep .mat-mdc-form-field .mdc-notched-outline__notch,.c-query-bar ::ng-deep .mat-mdc-form-field .mdc-notched-outline__trailing{border-color:#d6d8db!important;transition:border-color .12s ease}.c-query-bar ::ng-deep .mat-mdc-form-field .mdc-notched-outline__leading{border-radius:6px 0 0 6px!important}.c-query-bar ::ng-deep .mat-mdc-form-field .mdc-notched-outline__trailing{border-radius:0 6px 6px 0!important}.c-query-bar ::ng-deep .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__leading,.c-query-bar ::ng-deep .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__notch,.c-query-bar ::ng-deep .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__trailing{border-color:#b9bdc3!important}.c-query-bar ::ng-deep .mat-mdc-form-field.mat-focused .mdc-notched-outline__leading,.c-query-bar ::ng-deep .mat-mdc-form-field.mat-focused .mdc-notched-outline__notch,.c-query-bar ::ng-deep .mat-mdc-form-field.mat-focused .mdc-notched-outline__trailing{border-color:var(--sd-primary)!important;border-width:1px!important}.c-query-bar ::ng-deep .mat-mdc-form-field .mat-mdc-select-arrow,.c-query-bar ::ng-deep .mat-mdc-form-field .mat-mdc-form-field-icon-suffix sd-icon{color:#5f6368}.c-query-bar ::ng-deep .mat-mdc-form-field.mat-focused .mat-mdc-select-arrow{color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mdc-notched-outline__trailing,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mdc-notched-outline__trailing{border-color:#d6d8db!important;transition:border-color .12s ease}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mdc-notched-outline__leading{border-radius:6px 0 0 6px!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mdc-notched-outline__trailing,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mdc-notched-outline__trailing{border-radius:0 6px 6px 0!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__trailing,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__trailing{border-color:#b9bdc3!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field.mat-focused .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field.mat-focused .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field.mat-focused .mdc-notched-outline__trailing,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field.mat-focused .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field.mat-focused .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field.mat-focused .mdc-notched-outline__trailing{border-color:var(--sd-primary)!important;border-width:1px!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mat-mdc-select-arrow,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mat-mdc-form-field-icon-suffix sd-icon,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mat-mdc-select-arrow,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mat-mdc-form-field-icon-suffix sd-icon{color:#5f6368}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field.mat-focused .mat-mdc-select-arrow,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field.mat-focused .mat-mdc-select-arrow{color:var(--sd-primary)}\n"] }]
1537
+ ], template: "<!--\n sd-query-bar \u2014 unified filter chip row (Jira/Linear/Notion style).\n Visual spec: refs/design_handoff_sd_query_bar/README.md\n TODO(next-iter): replace inline placeholders for field-picker + chip-popover\n with extracted sub-components driven by CDK overlay.\n-->\n@let _filters = filters();\n@let _resolvedFields = resolvedFields();\n@let _resolvedDensity = resolvedDensity();\n@let _showOrConnector = showOrConnector();\n@let _usedFieldKeys = usedFieldKeys();\n@let _editingIndex = editingIndex();\n@let _resolvedMode = resolvedMode();\n@let _resolvedShowOperatorOnChip = resolvedShowOperatorOnChip();\n@let _noFieldsLabel = noFieldsLabel();\n@let _addFilterLabel = addFilterLabel();\n\n<div\n class=\"c-query-bar\"\n [class.c-density-compact]=\"_resolvedDensity === 'compact'\"\n [class.c-density-comfortable]=\"_resolvedDensity === 'comfortable'\">\n\n <!-- Main region \u2014 search + filters + add-filter. Wraps internally; the right\n action toolbar stays anchored and does NOT flow with this. -->\n <div class=\"c-query-bar__main\">\n\n <!-- Free-text search (left, optional) -->\n @if (resolvedShowSearch()) {\n <div class=\"c-search\">\n <sd-icon class=\"c-search-icon\" name=\"search\"></sd-icon>\n <input\n class=\"c-search-input\"\n type=\"text\"\n [ngModel]=\"search()\"\n (ngModelChange)=\"setSearch($event)\"\n (keydown.enter)=\"triggerApply()\"\n [placeholder]=\"searchPlaceholder()\" />\n </div>\n <span class=\"c-divider\" aria-hidden=\"true\"></span>\n }\n\n <!-- Chip popover \u2014 extracted into <sd-query-chip-popover>. Declared BEFORE chips\n so the popover-mode @for chips can bind their [menu] to `chipPopover.menu()`.\n why: m\u1EDF popover \u0111\u1EC3 EDIT ch\u1EC9 cho ch\u1EC9nh operator + value \u2014 KH\u00D4NG \u0111\u1ED5i field.\n B\u1ECF [switchPickerMenu] \u2192 header render nh\u00E3n field t\u0129nh (read-only). Mu\u1ED1n \u0111\u1ED5i\n field th\u00EC xo\u00E1 chip r\u1ED3i th\u00EAm l\u1EA1i. -->\n <sd-query-chip-popover\n #chipPopover\n [field]=\"editingField() ?? undefined\"\n [filter]=\"_editingIndex !== null ? $any(_filters[_editingIndex!]) : undefined\"\n [chipIndex]=\"_editingIndex\"\n [autoIdBase]=\"resolvedAutoId() ?? 'qb'\"\n (commit)=\"onChipPopoverCommit($event)\" />\n\n @if (_resolvedMode === 'inline') {\n <!-- ===== INLINE MODE (GitLab-style token builder) ===== -->\n\n <!-- Completed chips.\n why: track theo rowId(filter) ch\u1EE9 KH\u00D4NG theo $index \u2014 track $index g\u1EAFn chip v\u1EDBi v\u1ECB tr\u00ED,\n n\u00EAn xo\u00E1 filter \u0111\u1EA7u ti\u00EAn s\u1EBD bind l\u1EA1i chip c\u0169 sang filter k\u1EBF ti\u1EBFp v\u00E0 k\u00E9o theo state n\u1ED9i b\u1ED9\n (inline-chip `#editing`, inline-value-chip `draft`) sang sai filter. -->\n @for (filter of _filters; track rowId(filter); let i = $index) {\n @if (_showOrConnector && i > 0) {\n <span class=\"c-or-connector\" aria-hidden=\"true\">OR</span>\n }\n @let _field = fieldByKey()[$any(filter).field];\n @if (_field) {\n @let _op = $any(filter.operator);\n @let _data = $any(filter).data;\n @if (_field.type === 'string' || _field.type === 'number') {\n <!-- Seamless chip \u2014 the pill is the input (string / number). -->\n <sd-query-inline-value-chip\n [field]=\"_field\"\n [operator]=\"_op\"\n [value]=\"_data\"\n [density]=\"_resolvedDensity\"\n [showOperator]=\"_resolvedShowOperatorOnChip\"\n [autoId]=\"inlineAutoId(i, 'value')\"\n (valueChange)=\"updateFilterData(i, $event)\"\n (remove)=\"removeFilter(i)\" />\n } @else {\n <sd-query-inline-chip\n [field]=\"_field\"\n [filter]=\"filter\"\n [density]=\"_resolvedDensity\"\n [multiple]=\"multiple(_op)\"\n [showOperator]=\"_resolvedShowOperatorOnChip\"\n [autoId]=\"inlineAutoId(i, 'value')\"\n [valueText]=\"chipValueText(filter)\"\n [isNoData]=\"isNoDataOperator(_op)\"\n (commit)=\"updateFilterData(i, $event)\"\n (commitRange)=\"setFilterRange(i, $event)\"\n (liveChange)=\"updateFilterData(i, $event)\"\n (remove)=\"removeFilter(i)\" />\n }\n }\n }\n\n <!-- Building chip (in-progress) \u2014 extracted into <sd-query-build-chip>. -->\n @if (building(); as _b) {\n <sd-query-build-chip\n [building]=\"_b\"\n [density]=\"_resolvedDensity\"\n [allowedOperators]=\"allowedOperatorsFor(_b.field)\"\n [multiple]=\"multiple($any(_b.operator))\"\n [showOperator]=\"_resolvedShowOperatorOnChip\"\n (pickOperator)=\"pickBuildOperator($any($event))\"\n (commitValue)=\"commitBuildValue($event)\"\n (cancel)=\"cancelBuild()\"\n (seamlessCommit)=\"onBuildSeamlessCommit($event)\" />\n }\n\n <!-- Add filter -->\n <button\n type=\"button\"\n class=\"c-add-filter\"\n [matMenuTriggerFor]=\"addPicker.menu()\"\n [disabled]=\"_resolvedFields.length === 0 || _usedFieldKeys.size >= _resolvedFields.length\"\n [matTooltip]=\"_resolvedFields.length === 0 ? _noFieldsLabel : _addFilterLabel\">\n <sd-icon name=\"add\"></sd-icon>\n </button>\n } @else {\n <!-- ===== POPOVER MODE (default) \u2014 compact chips edited via mat-menu ===== -->\n <!-- why: track theo rowId(filter) \u2014 xem ghi ch\u00FA \u1EDF nh\u00E1nh inline. -->\n @for (filter of _filters; track rowId(filter); let i = $index) {\n @if (_showOrConnector && i > 0) {\n <span class=\"c-or-connector\" aria-hidden=\"true\">OR</span>\n }\n @let _field = fieldByKey()[$any(filter).field];\n @let _active = isFilterActive(filter);\n <!-- why: menu close commit is handled by chipPopover's (commit) \u2192\n onChipPopoverCommit; popover-chip only signals open + remove here. -->\n <sd-query-popover-chip\n [field]=\"_field\"\n [filter]=\"filter\"\n [active]=\"_active\"\n [showOperator]=\"_resolvedShowOperatorOnChip\"\n [valueText]=\"chipValueText(filter)\"\n [menu]=\"chipPopover.menu()!\"\n (open)=\"openChipPopover(i)\"\n (remove)=\"removeFilter(i)\" />\n }\n\n <!-- Add filter trigger -->\n <button\n type=\"button\"\n class=\"c-add-filter\"\n [matMenuTriggerFor]=\"addPicker.menu()\"\n [disabled]=\"_resolvedFields.length === 0 || _usedFieldKeys.size >= _resolvedFields.length\"\n [matTooltip]=\"_resolvedFields.length === 0 ? _noFieldsLabel : _addFilterLabel\">\n <sd-icon name=\"add\"></sd-icon>\n </button>\n }\n\n </div><!-- /.c-query-bar__main -->\n\n <!-- Field picker \u2014 list available fields, fade ones already in use. -->\n <sd-query-field-picker\n #addPicker\n [fields]=\"_resolvedFields\"\n [usedKeys]=\"_usedFieldKeys\"\n (pick)=\"_resolvedMode === 'inline' ? beginBuild($event) : addFilter($event)\" />\n\n <sd-query-actions-bar\n [showLogicToggle]=\"resolvedShowLogicToggle()\"\n [(logic)]=\"logic\"\n [filtersCount]=\"_filters.length\"\n [showSavedFilters]=\"resolvedShowSavedFilters()\"\n [savedFiltersKey]=\"resolvedSavedFiltersKey()\"\n [query]=\"currentQuery()\"\n (applyFilter)=\"onApplyFilter($event)\"\n [showClearAll]=\"resolvedShowClearAll()\"\n [canSearch]=\"canSearch()\"\n (clear)=\"clearAll()\"\n (search)=\"triggerApply()\" />\n</div>\n", styles: [":host{display:block;font-family:Roboto,system-ui,sans-serif}.c-query-bar{display:flex;align-items:flex-start;flex-wrap:nowrap;gap:8px;background:#fff;border:1px solid #e2e4e7;border-radius:8px;box-shadow:0 1px 2px #0f172a0a}.c-query-bar.c-density-compact{padding:8px}.c-query-bar.c-density-comfortable{padding:10px}.c-query-bar__main{flex:1 1 auto;min-width:0;display:flex;align-items:center;flex-wrap:wrap;gap:6px}.c-search{display:flex;align-items:center;gap:6px;min-width:220px;padding:0 10px;border:1px solid #d6d8db;border-radius:6px;background:#fff}.c-density-compact .c-search{height:28px}.c-density-comfortable .c-search{height:32px}.c-search .c-search-icon{font-size:16px;width:16px;height:16px;color:#7a7a7a}.c-search .c-search-input{flex:1;border:none;outline:none;background:transparent;font-size:13px;color:#212121}.c-search .c-search-input::placeholder{color:#7a7a7a}.c-search .c-search-input:focus-visible{outline:2px solid var(--sd-primary);outline-offset:1px;border-radius:4px}.c-search .c-search-kbd{font-family:Roboto Mono,monospace;font-size:10px;padding:1px 4px;border:1px solid #e2e4e7;border-radius:3px;color:#7a7a7a}.c-divider{width:1px;height:16px;background:#e2e4e7;margin:0 2px}.c-token{display:inline-flex;align-items:center;gap:4px;padding:0 0 0 12px;border:1px solid var(--sd-primary-light);border-radius:999px;background:var(--sd-primary-light);font-size:13px;color:#5f6368;white-space:nowrap;overflow:hidden}.c-density-compact .c-token{height:28px}.c-density-comfortable .c-token{height:32px}.c-token-icon{font-size:14px;width:14px;height:14px;line-height:14px;color:inherit}.c-token-field{font-weight:500;line-height:1}.c-token-op{display:inline-flex;align-items:center;color:inherit}.c-token-op ::ng-deep .c-op-trigger{padding:0;color:inherit}.c-token-op ::ng-deep .c-op-icon svg{width:14px;height:14px;display:block}.c-token-value-edit sd-select,.c-token-value-edit sd-date,.c-token-value-edit sd-datetime{display:inline-flex;align-items:center;line-height:1}.c-token ::ng-deep .mat-mdc-form-field{line-height:1}.c-token ::ng-deep .mat-mdc-text-field-wrapper,.c-token ::ng-deep .mat-mdc-form-field-flex{padding:0;min-height:0;background:transparent}.c-token ::ng-deep .mat-mdc-form-field-infix{padding:0;min-height:0;width:auto;border:0;display:inline-flex;align-items:center}.c-token ::ng-deep .mat-mdc-select-value,.c-token ::ng-deep .mat-mdc-input-element,.c-token ::ng-deep input{line-height:1;font-size:13px;padding:0;height:auto;color:inherit}.c-token ::ng-deep .mat-mdc-select-value{display:inline-flex;align-items:center;min-width:0;max-width:200px;overflow:hidden;text-overflow:ellipsis;color:var(--sd-primary);font-weight:500}.c-token ::ng-deep .sd-suffix-icon{display:none}.c-token-sep{color:#7a7a7a}.c-token-dash{color:#7a7a7a;padding:0 4px;line-height:1}.c-token-value{border:none;background:transparent;padding:0;font:inherit;font-weight:500;color:var(--sd-primary);cursor:pointer}.c-token-value:hover{text-decoration:underline}.c-token-value-edit{display:inline-flex;align-items:center;gap:4px;max-width:240px}.c-token-value-edit sd-input,.c-token-value-edit sd-input-number{display:inline-flex;max-width:140px}.c-token-value-edit sd-date-range{display:inline-flex;max-width:220px;min-width:0;flex:0 1 auto}.c-token-multi-head{display:inline-block;max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom}.c-token-remove{align-self:stretch;display:inline-flex;align-items:center;justify-content:center;border:none;border-left:1px solid rgba(0,0,0,.04);border-radius:0;background:transparent;cursor:pointer;color:inherit}.c-density-compact .c-token-remove{width:26px}.c-density-comfortable .c-token-remove{width:28px}.c-token-remove sd-icon{font-size:14px;width:14px;height:14px}.c-token-remove:hover{background:#0000000d}.c-add-filter{display:inline-flex;align-items:center;justify-content:center;padding:0;border-radius:999px;border:1px dashed #d6d8db;background:transparent;cursor:pointer;color:#5f6368;transition:border-color .12s ease,color .12s ease,background-color .12s ease}.c-density-compact .c-add-filter{width:28px;height:28px}.c-density-comfortable .c-add-filter{width:32px;height:32px}.c-add-filter sd-icon{font-size:18px;width:18px;height:18px}.c-add-filter:hover:not([disabled]){border-color:var(--sd-primary);color:var(--sd-primary)}.c-add-filter[disabled]{opacity:.5;cursor:not-allowed}.c-or-connector{font-family:Roboto Mono,monospace;font-size:10px;font-weight:700;letter-spacing:.6px;color:#7a7a7a;padding:0 2px}::ng-deep .mat-mdc-menu-panel.c-chip-popover{min-width:280px;max-width:320px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header{display:flex;align-items:center;gap:8px;padding:10px 12px;border-bottom:1px solid #e2e4e7;font-size:13px;font-weight:500;color:#212121}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header sd-icon{font-size:16px;width:16px;height:16px;color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header .c-pop-header-operator{margin-left:auto}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field{display:inline-flex;align-items:center;gap:4px;padding:2px 4px 2px 6px;margin-left:-4px;background:transparent;border:1px solid transparent;border-radius:4px;font:inherit;font-weight:500;color:#212121;cursor:pointer;transition:background-color .12s ease,border-color .12s ease}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field sd-icon{font-size:14px!important;width:14px!important;height:14px!important;color:#7a7a7a!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-header-field:hover{background:#f5f6f7;border-color:#e2e4e7}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-body{padding:10px 12px;display:flex;flex-direction:column;gap:12px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-input{width:100%;height:32px;padding:0 10px;border:1px solid #d6d8db;border-radius:6px;background:#fff;font-family:inherit;font-size:13px;color:#212121;outline:none}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-input:focus{border-color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-input:focus-visible{outline:2px solid var(--sd-primary);outline-offset:1px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-between{display:grid;grid-template-columns:1fr 1fr;gap:8px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-boolean{display:flex;gap:8px}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-boolean button{flex:1;height:32px;border:1px solid #d6d8db;border-radius:6px;background:#fff;font:inherit;font-size:13px;cursor:pointer}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-boolean button.c-pop-boolean-active{background:var(--sd-primary-light);border-color:var(--sd-primary);color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values{max-height:220px;overflow-y:auto;margin:0;padding:0;list-style:none}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values li{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:4px;font-size:13px;cursor:pointer;-webkit-user-select:none;user-select:none}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values li:hover{background:#f5f6f7}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values li.c-pop-value-selected{background:var(--sd-primary-light);color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values li input[type=checkbox],::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values li input[type=radio]{accent-color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values-loading,::ng-deep .mat-mdc-menu-panel.c-chip-popover .c-pop-values-empty{padding:10px 8px;font-size:12px;color:#7a7a7a}.c-bool-toggle{display:inline-flex;align-items:center;gap:4px}.c-bool-toggle ::ng-deep .mdc-button,.c-bool-toggle ::ng-deep button[mat-button],.c-bool-toggle ::ng-deep button[mat-stroked-button],.c-bool-toggle ::ng-deep button[mat-flat-button]{border-radius:999px;min-width:0;padding:0 12px}\n", ".c-query-bar ::ng-deep .mat-mdc-form-field .mdc-notched-outline__leading,.c-query-bar ::ng-deep .mat-mdc-form-field .mdc-notched-outline__notch,.c-query-bar ::ng-deep .mat-mdc-form-field .mdc-notched-outline__trailing{border-color:#d6d8db!important;transition:border-color .12s ease}.c-query-bar ::ng-deep .mat-mdc-form-field .mdc-notched-outline__leading{border-radius:6px 0 0 6px!important}.c-query-bar ::ng-deep .mat-mdc-form-field .mdc-notched-outline__trailing{border-radius:0 6px 6px 0!important}.c-query-bar ::ng-deep .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__leading,.c-query-bar ::ng-deep .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__notch,.c-query-bar ::ng-deep .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__trailing{border-color:#b9bdc3!important}.c-query-bar ::ng-deep .mat-mdc-form-field.mat-focused .mdc-notched-outline__leading,.c-query-bar ::ng-deep .mat-mdc-form-field.mat-focused .mdc-notched-outline__notch,.c-query-bar ::ng-deep .mat-mdc-form-field.mat-focused .mdc-notched-outline__trailing{border-color:var(--sd-primary)!important;border-width:1px!important}.c-query-bar ::ng-deep .mat-mdc-form-field .mat-mdc-select-arrow,.c-query-bar ::ng-deep .mat-mdc-form-field .mat-mdc-form-field-icon-suffix sd-icon{color:#5f6368}.c-query-bar ::ng-deep .mat-mdc-form-field.mat-focused .mat-mdc-select-arrow{color:var(--sd-primary)}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mdc-notched-outline__trailing,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mdc-notched-outline__trailing{border-color:#d6d8db!important;transition:border-color .12s ease}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mdc-notched-outline__leading{border-radius:6px 0 0 6px!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mdc-notched-outline__trailing,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mdc-notched-outline__trailing{border-radius:0 6px 6px 0!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__trailing,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field:hover:not(.mat-focused):not(.mat-form-field-disabled) .mdc-notched-outline__trailing{border-color:#b9bdc3!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field.mat-focused .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field.mat-focused .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field.mat-focused .mdc-notched-outline__trailing,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field.mat-focused .mdc-notched-outline__leading,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field.mat-focused .mdc-notched-outline__notch,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field.mat-focused .mdc-notched-outline__trailing{border-color:var(--sd-primary)!important;border-width:1px!important}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mat-mdc-select-arrow,::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field .mat-mdc-form-field-icon-suffix sd-icon,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mat-mdc-select-arrow,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field .mat-mdc-form-field-icon-suffix sd-icon{color:#5f6368}::ng-deep .mat-mdc-menu-panel.c-chip-popover .mat-mdc-form-field.mat-focused .mat-mdc-select-arrow,::ng-deep .mat-mdc-menu-panel.c-field-picker-menu .mat-mdc-form-field.mat-focused .mat-mdc-select-arrow{color:var(--sd-primary)}\n"] }]
1376
1538
  }], ctorParameters: () => [], propDecorators: { popoverChips: [{ type: i0.ViewChildren, args: [i0.forwardRef(() => SdQueryPopoverChip), { isSignal: true }] }], buildChip: [{ type: i0.ViewChild, args: [i0.forwardRef(() => SdQueryBuildChip), { isSignal: true }] }], chipPopover: [{ type: i0.ViewChild, args: [i0.forwardRef(() => SdQueryChipPopover), { isSignal: true }] }], option: [{ type: i0.Input, args: [{ isSignal: true, alias: "option", required: false }] }], autoIdInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoId", required: false }] }], fields: [{ type: i0.Input, args: [{ isSignal: true, alias: "fields", required: false }] }], filters: [{ type: i0.Input, args: [{ isSignal: true, alias: "filters", required: false }] }, { type: i0.Output, args: ["filtersChange"] }], logic: [{ type: i0.Input, args: [{ isSignal: true, alias: "logic", required: false }] }, { type: i0.Output, args: ["logicChange"] }], search: [{ type: i0.Input, args: [{ isSignal: true, alias: "search", required: false }] }, { type: i0.Output, args: ["searchChange"] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], density: [{ type: i0.Input, args: [{ isSignal: true, alias: "density", required: false }] }], showSearch: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSearch", required: false }] }], showSavedFilters: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSavedFilters", required: false }] }], savedFiltersKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "savedFiltersKey", required: false }] }], showLogicToggle: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLogicToggle", required: false }] }], showClearAll: [{ type: i0.Input, args: [{ isSignal: true, alias: "showClearAll", required: false }] }], showOperatorOnChip: [{ type: i0.Input, args: [{ isSignal: true, alias: "showOperatorOnChip", required: false }] }], queryChange: [{ type: i0.Output, args: ["queryChange"] }], apply: [{ type: i0.Output, args: ["apply"] }] } });
1377
1539
 
1378
1540
  /**