@sdcorejs/angular 19.1.5 → 19.2.0

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 (361) hide show
  1. package/README.md +12 -4
  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/components/anchor/src/components/anchor/anchor.component.d.ts +3 -1
  6. package/components/badge/src/badge.component.d.ts +1 -0
  7. package/components/ckeditor-styles/src/ckeditor-styles.component.d.ts +1 -1
  8. package/components/form-generic/src/components/form-builder/components/expression-builder/expression-builder.component.d.ts +2 -1
  9. package/components/form-generic/src/components/form-builder/form-builder.component.d.ts +3 -1
  10. package/components/form-generic/src/components/form-render/components/item/components/checkbox/checkbox.component.d.ts +34 -0
  11. package/components/form-generic/src/components/form-render/components/item/components/index.d.ts +1 -0
  12. package/components/form-generic/src/components/form-render/components/item/components/table/table.component.d.ts +6 -1
  13. package/components/form-generic/src/components/sd-feel-expression/sd-feel-expression.component.d.ts +1 -1
  14. package/components/form-generic/src/configurations/form.configuration.d.ts +1 -1
  15. package/components/form-generic/src/models/form-generic-component.model.d.ts +8 -8
  16. package/components/form-generic/src/models/form-generic-definition-selection.model.d.ts +5 -5
  17. package/components/form-generic/src/models/form-generic-expression.model.d.ts +12 -12
  18. package/components/form-generic/src/models/index.d.ts +1 -0
  19. package/components/form-generic/src/services/form-generic.service.d.ts +2 -2
  20. package/components/import-excel/src/import-excel.component.d.ts +1 -0
  21. package/components/index.d.ts +6 -1
  22. package/components/mini-editor/src/mini-editor.model.d.ts +1 -1
  23. package/components/modal/src/modal.component.d.ts +3 -2
  24. package/components/modal-resizable/src/modal-resizable.component.d.ts +92 -0
  25. package/components/modal-resizable/src/modal-resizable.registry.d.ts +22 -0
  26. package/components/org-chart/src/org-chart.component.d.ts +6 -0
  27. package/components/preview/src/preview-image/preview-image.component.d.ts +1 -0
  28. package/components/preview/src/preview-pdf/pdf-worker-inline.generated.d.ts +7 -0
  29. package/components/query-bar/src/components/actions-bar/actions-bar.component.d.ts +6 -2
  30. package/components/query-bar/src/components/build-chip/build-chip.component.d.ts +4 -1
  31. package/components/query-bar/src/components/chip-popover/chip-popover.component.d.ts +8 -0
  32. package/components/query-bar/src/components/field-picker/field-picker.component.d.ts +2 -0
  33. package/components/query-bar/src/components/inline-chip/inline-chip.component.d.ts +2 -0
  34. package/components/query-bar/src/components/inline-value-chip/inline-value-chip.component.d.ts +3 -3
  35. package/components/query-bar/src/components/saved-filters-menu/saved-filters-menu.component.d.ts +9 -4
  36. package/components/query-bar/src/query-bar.component.d.ts +32 -5
  37. package/components/query-bar/src/query-bar.model.d.ts +15 -12
  38. package/components/query-builder/src/query-builder.component.d.ts +76 -39
  39. package/components/query-builder/src/query-builder.model.d.ts +59 -52
  40. package/components/query-builder/src/query-builder.serializer.d.ts +15 -6
  41. package/components/section/src/section.component.d.ts +3 -0
  42. package/components/side-drawer/src/body-scroll-lock.service.d.ts +10 -0
  43. package/components/splitter/src/splitter-handle/splitter-handle.component.d.ts +1 -0
  44. package/components/tab-router/index.d.ts +2 -1
  45. package/components/tab-router/src/actions/tab-router.action.d.ts +2 -2
  46. package/components/tab-router/src/components/tab-router-item/tab-router-item.component.d.ts +2 -2
  47. package/components/tab-router/src/components/tab-router-nav/tab-router-nav.component.d.ts +3 -3
  48. package/components/tab-router/src/components/tab-router-outlet/tab-router-outlet.component.d.ts +3 -3
  49. package/components/tab-router/src/decorators/tab.decorator.d.ts +13 -1
  50. package/components/tab-router/src/events/tab-router.event.d.ts +3 -3
  51. package/components/tab-router/src/models/tab-router.model.d.ts +2 -2
  52. package/components/tab-router/src/pipes/tab-info.pipe.d.ts +2 -2
  53. package/components/tab-router/src/services/tab-router.service.d.ts +9 -9
  54. package/components/table/index.d.ts +1 -0
  55. package/components/table/src/components/config/config.component.d.ts +6 -1
  56. package/components/table/src/components/desktop-cell/view/view.component.d.ts +1 -0
  57. package/components/table/src/components/filter/external-filter/external-filter.component.d.ts +1 -1
  58. package/components/table/src/components/filter/mobile-filter/mobile-filter.component.d.ts +1 -1
  59. package/components/table/src/components/selector-action/action-filter.pipe.d.ts +8 -1
  60. package/components/table/src/components/selector-action/selector-action.component.d.ts +2 -0
  61. package/components/table/src/directives/index.d.ts +1 -0
  62. package/components/table/src/directives/sd-table-command-header-def.directive.d.ts +24 -0
  63. package/components/table/src/models/table-column.model.d.ts +30 -13
  64. package/components/table/src/models/table-item.model.d.ts +30 -1
  65. package/components/table/src/models/table-option-selector.model.d.ts +5 -4
  66. package/components/table/src/models/table-option.model.d.ts +12 -0
  67. package/components/table/src/pipes/sd-group.pipe.d.ts +6 -2
  68. package/components/table/src/services/table-export/table-export.service.d.ts +3 -1
  69. package/components/table/src/services/table-filter/table-filter.model.d.ts +15 -15
  70. package/components/table/src/services/table-filter/table-filter.service.d.ts +2 -2
  71. package/components/table/src/services/table-format/table-format.service.d.ts +1 -1
  72. package/components/table/src/services/table-selection/selection-action.util.d.ts +24 -0
  73. package/components/table/src/services/table-selection/table-selection.util.d.ts +31 -0
  74. package/components/table/src/services/tree/tree.util.d.ts +3 -2
  75. package/components/table/src/table.component.d.ts +52 -6
  76. package/components/tree/src/tree-item-def.directive.d.ts +1 -1
  77. package/components/tree/src/tree.component.d.ts +16 -4
  78. package/components/tree/src/tree.model.d.ts +31 -25
  79. package/components/upload-file/src/configurations/upload-file.configuration.d.ts +2 -2
  80. package/components/upload-file/src/upload-file.component.d.ts +4 -1
  81. package/configurations/src/sd-core.configuration.d.ts +1 -2
  82. package/fesm2022/sdcorejs-angular-components-anchor.mjs +36 -12
  83. package/fesm2022/sdcorejs-angular-components-anchor.mjs.map +1 -1
  84. package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs +2 -2
  85. package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs.map +1 -1
  86. package/fesm2022/sdcorejs-angular-components-badge.mjs +15 -2
  87. package/fesm2022/sdcorejs-angular-components-badge.mjs.map +1 -1
  88. package/fesm2022/sdcorejs-angular-components-button.mjs +2 -2
  89. package/fesm2022/sdcorejs-angular-components-button.mjs.map +1 -1
  90. package/fesm2022/sdcorejs-angular-components-ckeditor-styles.mjs +1 -1
  91. package/fesm2022/sdcorejs-angular-components-ckeditor-styles.mjs.map +1 -1
  92. package/fesm2022/sdcorejs-angular-components-code-editor.mjs +3 -3
  93. package/fesm2022/sdcorejs-angular-components-code-editor.mjs.map +1 -1
  94. package/fesm2022/sdcorejs-angular-components-data-state.mjs +3 -3
  95. package/fesm2022/sdcorejs-angular-components-data-state.mjs.map +1 -1
  96. package/fesm2022/sdcorejs-angular-components-editor.mjs +2 -2
  97. package/fesm2022/sdcorejs-angular-components-editor.mjs.map +1 -1
  98. package/fesm2022/sdcorejs-angular-components-form-generic.mjs +324 -192
  99. package/fesm2022/sdcorejs-angular-components-form-generic.mjs.map +1 -1
  100. package/fesm2022/sdcorejs-angular-components-history.mjs +4 -4
  101. package/fesm2022/sdcorejs-angular-components-history.mjs.map +1 -1
  102. package/fesm2022/sdcorejs-angular-components-import-excel.mjs +13 -5
  103. package/fesm2022/sdcorejs-angular-components-import-excel.mjs.map +1 -1
  104. package/fesm2022/sdcorejs-angular-components-inform.mjs +3 -3
  105. package/fesm2022/sdcorejs-angular-components-inform.mjs.map +1 -1
  106. package/fesm2022/sdcorejs-angular-components-modal-resizable.mjs +164 -8
  107. package/fesm2022/sdcorejs-angular-components-modal-resizable.mjs.map +1 -1
  108. package/fesm2022/sdcorejs-angular-components-modal.mjs +30 -18
  109. package/fesm2022/sdcorejs-angular-components-modal.mjs.map +1 -1
  110. package/fesm2022/sdcorejs-angular-components-org-chart.mjs +60 -13
  111. package/fesm2022/sdcorejs-angular-components-org-chart.mjs.map +1 -1
  112. package/fesm2022/sdcorejs-angular-components-preview.mjs +58 -19
  113. package/fesm2022/sdcorejs-angular-components-preview.mjs.map +1 -1
  114. package/fesm2022/sdcorejs-angular-components-query-bar.mjs +192 -30
  115. package/fesm2022/sdcorejs-angular-components-query-bar.mjs.map +1 -1
  116. package/fesm2022/sdcorejs-angular-components-query-builder.mjs +187 -127
  117. package/fesm2022/sdcorejs-angular-components-query-builder.mjs.map +1 -1
  118. package/fesm2022/sdcorejs-angular-components-section.mjs +16 -2
  119. package/fesm2022/sdcorejs-angular-components-section.mjs.map +1 -1
  120. package/fesm2022/sdcorejs-angular-components-side-drawer.mjs +83 -24
  121. package/fesm2022/sdcorejs-angular-components-side-drawer.mjs.map +1 -1
  122. package/fesm2022/sdcorejs-angular-components-splitter.mjs +57 -21
  123. package/fesm2022/sdcorejs-angular-components-splitter.mjs.map +1 -1
  124. package/fesm2022/sdcorejs-angular-components-stepper.mjs +2 -2
  125. package/fesm2022/sdcorejs-angular-components-stepper.mjs.map +1 -1
  126. package/fesm2022/sdcorejs-angular-components-tab-router.mjs +68 -20
  127. package/fesm2022/sdcorejs-angular-components-tab-router.mjs.map +1 -1
  128. package/fesm2022/sdcorejs-angular-components-tab.mjs +3 -2
  129. package/fesm2022/sdcorejs-angular-components-tab.mjs.map +1 -1
  130. package/fesm2022/sdcorejs-angular-components-table.mjs +556 -254
  131. package/fesm2022/sdcorejs-angular-components-table.mjs.map +1 -1
  132. package/fesm2022/sdcorejs-angular-components-tree.mjs +80 -17
  133. package/fesm2022/sdcorejs-angular-components-tree.mjs.map +1 -1
  134. package/fesm2022/sdcorejs-angular-components-upload-file.mjs +48 -13
  135. package/fesm2022/sdcorejs-angular-components-upload-file.mjs.map +1 -1
  136. package/fesm2022/sdcorejs-angular-components.mjs +6 -1
  137. package/fesm2022/sdcorejs-angular-components.mjs.map +1 -1
  138. package/fesm2022/sdcorejs-angular-configurations.mjs.map +1 -1
  139. package/fesm2022/sdcorejs-angular-directives.mjs +45 -8
  140. package/fesm2022/sdcorejs-angular-directives.mjs.map +1 -1
  141. package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs +40 -29
  142. package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs.map +1 -1
  143. package/fesm2022/sdcorejs-angular-forms-checkbox.mjs +5 -5
  144. package/fesm2022/sdcorejs-angular-forms-checkbox.mjs.map +1 -1
  145. package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs +67 -50
  146. package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs.map +1 -1
  147. package/fesm2022/sdcorejs-angular-forms-chip.mjs +57 -43
  148. package/fesm2022/sdcorejs-angular-forms-chip.mjs.map +1 -1
  149. package/fesm2022/sdcorejs-angular-forms-date-range.mjs +233 -63
  150. package/fesm2022/sdcorejs-angular-forms-date-range.mjs.map +1 -1
  151. package/fesm2022/sdcorejs-angular-forms-date.mjs +208 -41
  152. package/fesm2022/sdcorejs-angular-forms-date.mjs.map +1 -1
  153. package/fesm2022/sdcorejs-angular-forms-datetime.mjs +130 -26
  154. package/fesm2022/sdcorejs-angular-forms-datetime.mjs.map +1 -1
  155. package/fesm2022/sdcorejs-angular-forms-entity-picker.mjs +39 -15
  156. package/fesm2022/sdcorejs-angular-forms-entity-picker.mjs.map +1 -1
  157. package/fesm2022/sdcorejs-angular-forms-input-color.mjs +16 -5
  158. package/fesm2022/sdcorejs-angular-forms-input-color.mjs.map +1 -1
  159. package/fesm2022/sdcorejs-angular-forms-input-number.mjs +24 -9
  160. package/fesm2022/sdcorejs-angular-forms-input-number.mjs.map +1 -1
  161. package/fesm2022/sdcorejs-angular-forms-input.mjs +58 -10
  162. package/fesm2022/sdcorejs-angular-forms-input.mjs.map +1 -1
  163. package/fesm2022/sdcorejs-angular-forms-models.mjs +279 -6
  164. package/fesm2022/sdcorejs-angular-forms-models.mjs.map +1 -1
  165. package/fesm2022/sdcorejs-angular-forms-radio.mjs +4 -4
  166. package/fesm2022/sdcorejs-angular-forms-radio.mjs.map +1 -1
  167. package/fesm2022/sdcorejs-angular-forms-select.mjs +135 -37
  168. package/fesm2022/sdcorejs-angular-forms-select.mjs.map +1 -1
  169. package/fesm2022/sdcorejs-angular-forms-switch.mjs +18 -3
  170. package/fesm2022/sdcorejs-angular-forms-switch.mjs.map +1 -1
  171. package/fesm2022/sdcorejs-angular-forms-textarea.mjs +51 -62
  172. package/fesm2022/sdcorejs-angular-forms-textarea.mjs.map +1 -1
  173. package/fesm2022/sdcorejs-angular-forms-time-range.mjs +57 -12
  174. package/fesm2022/sdcorejs-angular-forms-time-range.mjs.map +1 -1
  175. package/fesm2022/sdcorejs-angular-forms-time.mjs +16 -5
  176. package/fesm2022/sdcorejs-angular-forms-time.mjs.map +1 -1
  177. package/fesm2022/sdcorejs-angular-forms-tree-select.mjs +100 -9
  178. package/fesm2022/sdcorejs-angular-forms-tree-select.mjs.map +1 -1
  179. package/fesm2022/sdcorejs-angular-forms.mjs +91 -46
  180. package/fesm2022/sdcorejs-angular-forms.mjs.map +1 -1
  181. package/fesm2022/sdcorejs-angular-handlers.mjs +18 -3
  182. package/fesm2022/sdcorejs-angular-handlers.mjs.map +1 -1
  183. package/fesm2022/sdcorejs-angular-i18n.mjs +510 -68
  184. package/fesm2022/sdcorejs-angular-i18n.mjs.map +1 -1
  185. package/fesm2022/sdcorejs-angular-interceptors.mjs +87 -29
  186. package/fesm2022/sdcorejs-angular-interceptors.mjs.map +1 -1
  187. package/fesm2022/sdcorejs-angular-modules-auth.mjs +25 -15
  188. package/fesm2022/sdcorejs-angular-modules-auth.mjs.map +1 -1
  189. package/fesm2022/sdcorejs-angular-modules-icon.mjs +2 -2
  190. package/fesm2022/sdcorejs-angular-modules-icon.mjs.map +1 -1
  191. package/fesm2022/sdcorejs-angular-modules-keycloak.mjs +32 -3
  192. package/fesm2022/sdcorejs-angular-modules-keycloak.mjs.map +1 -1
  193. package/fesm2022/sdcorejs-angular-modules-layout.mjs +441 -135
  194. package/fesm2022/sdcorejs-angular-modules-layout.mjs.map +1 -1
  195. package/fesm2022/sdcorejs-angular-modules-permission.mjs +177 -40
  196. package/fesm2022/sdcorejs-angular-modules-permission.mjs.map +1 -1
  197. package/fesm2022/sdcorejs-angular-pipes.mjs +62 -28
  198. package/fesm2022/sdcorejs-angular-pipes.mjs.map +1 -1
  199. package/fesm2022/sdcorejs-angular-services-api.mjs +123 -13
  200. package/fesm2022/sdcorejs-angular-services-api.mjs.map +1 -1
  201. package/fesm2022/sdcorejs-angular-services-cache.mjs +53 -3
  202. package/fesm2022/sdcorejs-angular-services-cache.mjs.map +1 -1
  203. package/fesm2022/sdcorejs-angular-services-confirm.mjs +25 -73
  204. package/fesm2022/sdcorejs-angular-services-confirm.mjs.map +1 -1
  205. package/fesm2022/sdcorejs-angular-services-excel.mjs +12 -10
  206. package/fesm2022/sdcorejs-angular-services-excel.mjs.map +1 -1
  207. package/fesm2022/sdcorejs-angular-services-loading.mjs +75 -84
  208. package/fesm2022/sdcorejs-angular-services-loading.mjs.map +1 -1
  209. package/fesm2022/sdcorejs-angular-services-notify.mjs +46 -5
  210. package/fesm2022/sdcorejs-angular-services-notify.mjs.map +1 -1
  211. package/fesm2022/sdcorejs-angular-services-storage.mjs +4 -0
  212. package/fesm2022/sdcorejs-angular-services-storage.mjs.map +1 -1
  213. package/fesm2022/sdcorejs-angular-services.mjs +0 -1
  214. package/fesm2022/sdcorejs-angular-services.mjs.map +1 -1
  215. package/fesm2022/sdcorejs-angular-utilities-extensions.mjs +225 -80
  216. package/fesm2022/sdcorejs-angular-utilities-extensions.mjs.map +1 -1
  217. package/fesm2022/sdcorejs-angular-utilities-models.mjs +1 -63
  218. package/fesm2022/sdcorejs-angular-utilities-models.mjs.map +1 -1
  219. package/fesm2022/sdcorejs-angular.mjs +17 -10
  220. package/fesm2022/sdcorejs-angular.mjs.map +1 -1
  221. package/forms/autocomplete/src/autocomplete.component.d.ts +5 -2
  222. package/forms/chip-calendar/src/chip-calendar.component.d.ts +10 -0
  223. package/forms/date/src/date-input.util.d.ts +18 -0
  224. package/forms/date/src/date.component.d.ts +21 -3
  225. package/forms/date-range/src/date-range.component.d.ts +13 -2
  226. package/forms/datetime/src/datetime.component.d.ts +13 -2
  227. package/forms/entity-picker/src/entity-picker.component.d.ts +9 -3
  228. package/forms/input/src/input.component.d.ts +3 -0
  229. package/forms/input-color/src/input-color.component.d.ts +4 -0
  230. package/forms/input-number/src/input-number.component.d.ts +3 -0
  231. package/forms/models/index.d.ts +4 -0
  232. package/forms/models/src/sd-form-control-connector.d.ts +12 -0
  233. package/forms/models/src/sd-model-facing-control.d.ts +43 -0
  234. package/forms/models/src/sd-search.model.d.ts +1 -1
  235. package/forms/models/src/sd-selection-data.model.d.ts +1 -1
  236. package/forms/models/src/sd-strict-date-adapter.d.ts +26 -0
  237. package/forms/models/src/sd-temporal-transform.d.ts +31 -0
  238. package/forms/models/src/sd-timer-scope.d.ts +29 -0
  239. package/forms/select/src/select.component.d.ts +27 -4
  240. package/forms/src/forms.module.d.ts +22 -13
  241. package/forms/switch/src/switch.component.d.ts +5 -0
  242. package/forms/textarea/src/textarea.component.d.ts +5 -3
  243. package/forms/time-range/src/time-range.component.d.ts +22 -1
  244. package/forms/tree-select/src/tree-select.component.d.ts +36 -5
  245. package/i18n/src/en.d.ts +94 -10
  246. package/i18n/src/i18n.messages.d.ts +1 -1
  247. package/i18n/src/i18n.pipe.d.ts +3 -3
  248. package/i18n/src/i18n.service.d.ts +2 -1
  249. package/i18n/src/i18n.types.d.ts +0 -3
  250. package/interceptors/no-internet/no-internet.interceptor.d.ts +11 -1
  251. package/modules/auth/services/auth.model.d.ts +1 -1
  252. package/modules/keycloak/keycloak.service.d.ts +1 -0
  253. package/modules/layout/components/shared/menu-tree/menu-tree.component.d.ts +2 -0
  254. package/modules/layout/components/sidebar-mobile-v1/components/sidebar/sidebar.component.d.ts +3 -3
  255. package/modules/layout/components/sidebar-mobile-v1/main.component.d.ts +3 -3
  256. package/modules/layout/components/sidebar-mobile-v2/main.component.d.ts +3 -3
  257. package/modules/layout/components/sidebar-mobile-v3/main.component.d.ts +3 -3
  258. package/modules/layout/components/sidebar-v1/components/sidebar/sidebar.component.d.ts +8 -3
  259. package/modules/layout/components/sidebar-v1/main.component.d.ts +3 -3
  260. package/modules/layout/components/sidebar-v2/main.component.d.ts +3 -3
  261. package/modules/layout/components/sidebar-v3/main.component.d.ts +3 -3
  262. package/modules/layout/index.d.ts +1 -0
  263. package/modules/layout/modules/forbidden/pages/root/root.component.d.ts +5 -0
  264. package/modules/layout/modules/not-found/pages/root/root.component.d.ts +5 -0
  265. package/modules/layout/services/layout.service.d.ts +21 -0
  266. package/modules/layout/services/storage/storage.service.d.ts +52 -7
  267. package/modules/layout/utils/index.d.ts +1 -0
  268. package/modules/layout/utils/tab-name.util.d.ts +8 -0
  269. package/modules/permission/src/configurations/permission.configuration.d.ts +11 -0
  270. package/modules/permission/src/directives/permission.directive.d.ts +2 -1
  271. package/modules/permission/src/services/permission.service.d.ts +52 -1
  272. package/package.json +48 -33
  273. package/pipes/src/safe-html.pipe.d.ts +26 -4
  274. package/pipes/src/time-different.pipe.d.ts +1 -0
  275. package/public-api.d.ts +11 -10
  276. package/services/api/src/api-host.d.ts +15 -0
  277. package/services/api/src/api.model.d.ts +14 -0
  278. package/services/api/src/api.module.d.ts +6 -0
  279. package/services/cache/src/cache.model.d.ts +10 -0
  280. package/services/index.d.ts +0 -1
  281. package/services/notify/index.d.ts +1 -1
  282. package/utilities/extensions/index.d.ts +1 -5
  283. package/utilities/extensions/src/url-safety.d.ts +72 -0
  284. package/utilities/extensions/src/utility.extension.d.ts +9 -5
  285. package/utilities/models/index.d.ts +0 -10
  286. package/components/base/index.d.ts +0 -1
  287. package/components/base/src/base-secure.component.d.ts +0 -8
  288. package/components/document-builder/index.d.ts +0 -4
  289. package/components/document-builder/src/components/header-footer-builder/header-footer-builder.component.d.ts +0 -15
  290. package/components/document-builder/src/components/header-footer-builder/plugins/page-number.plugin.d.ts +0 -4
  291. package/components/document-builder/src/components/index.d.ts +0 -1
  292. package/components/document-builder/src/document-builder.component.d.ts +0 -53
  293. package/components/document-builder/src/document-builder.config.d.ts +0 -21
  294. package/components/document-builder/src/document-builder.model.d.ts +0 -41
  295. package/components/document-builder/src/document-builder.utils.d.ts +0 -10
  296. package/components/document-builder/src/plugins/block-space/block-space.plugin.d.ts +0 -9
  297. package/components/document-builder/src/plugins/ck-comment/ck-comment.plugin.d.ts +0 -44
  298. package/components/document-builder/src/plugins/ck-comment/ck-comment.plugin.model.d.ts +0 -57
  299. package/components/document-builder/src/plugins/heading/heading.plugin.d.ts +0 -5
  300. package/components/document-builder/src/plugins/highlight-range/highlight-range.plugin.d.ts +0 -4
  301. package/components/document-builder/src/plugins/image-custom/image-custom.plugin.d.ts +0 -31
  302. package/components/document-builder/src/plugins/image-upload/image-upload.plugin.d.ts +0 -5
  303. package/components/document-builder/src/plugins/index.d.ts +0 -11
  304. package/components/document-builder/src/plugins/page-orientation/page-orientation.plugin.d.ts +0 -24
  305. package/components/document-builder/src/plugins/paste-handler/filters/bookmark.d.ts +0 -14
  306. package/components/document-builder/src/plugins/paste-handler/filters/br.d.ts +0 -15
  307. package/components/document-builder/src/plugins/paste-handler/filters/image.d.ts +0 -25
  308. package/components/document-builder/src/plugins/paste-handler/filters/list.d.ts +0 -29
  309. package/components/document-builder/src/plugins/paste-handler/filters/parse.d.ts +0 -35
  310. package/components/document-builder/src/plugins/paste-handler/filters/removeboldwrapper.d.ts +0 -15
  311. package/components/document-builder/src/plugins/paste-handler/filters/removegooglesheetstag.d.ts +0 -15
  312. package/components/document-builder/src/plugins/paste-handler/filters/removeinvalidtablewidth.d.ts +0 -15
  313. package/components/document-builder/src/plugins/paste-handler/filters/removemsattributes.d.ts +0 -15
  314. package/components/document-builder/src/plugins/paste-handler/filters/removestyleblock.d.ts +0 -15
  315. package/components/document-builder/src/plugins/paste-handler/filters/removexmlns.d.ts +0 -15
  316. package/components/document-builder/src/plugins/paste-handler/filters/replacemsfootnotes.d.ts +0 -54
  317. package/components/document-builder/src/plugins/paste-handler/filters/space.d.ts +0 -27
  318. package/components/document-builder/src/plugins/paste-handler/filters/table.d.ts +0 -16
  319. package/components/document-builder/src/plugins/paste-handler/filters/utils.d.ts +0 -25
  320. package/components/document-builder/src/plugins/paste-handler/index.d.ts +0 -35
  321. package/components/document-builder/src/plugins/paste-handler/normalizers/googledocsnormalizer.d.ts +0 -31
  322. package/components/document-builder/src/plugins/paste-handler/normalizers/googlesheetsnormalizer.d.ts +0 -31
  323. package/components/document-builder/src/plugins/paste-handler/normalizers/mswordnormalizer.d.ts +0 -29
  324. package/components/document-builder/src/plugins/paste-handler/types.d.ts +0 -30
  325. package/components/document-builder/src/plugins/table-custom/index.d.ts +0 -34
  326. package/components/document-builder/src/plugins/variable/variable.plugin.d.ts +0 -51
  327. package/components/table/src/pipes/selection-visible-select-all.pipe.d.ts +0 -9
  328. package/fesm2022/sdcorejs-angular-components-base.mjs +0 -23
  329. package/fesm2022/sdcorejs-angular-components-base.mjs.map +0 -1
  330. package/fesm2022/sdcorejs-angular-components-document-builder.mjs +0 -5038
  331. package/fesm2022/sdcorejs-angular-components-document-builder.mjs.map +0 -1
  332. package/fesm2022/sdcorejs-angular-models.mjs +0 -6
  333. package/fesm2022/sdcorejs-angular-models.mjs.map +0 -1
  334. package/fesm2022/sdcorejs-angular-services-docx.mjs +0 -291
  335. package/fesm2022/sdcorejs-angular-services-docx.mjs.map +0 -1
  336. package/fesm2022/sdcorejs-angular-services-license.mjs +0 -97
  337. package/fesm2022/sdcorejs-angular-services-license.mjs.map +0 -1
  338. package/models/index.d.ts +0 -1
  339. package/models/src/language.model.d.ts +0 -2
  340. package/services/docx/index.d.ts +0 -1
  341. package/services/docx/src/lib/docx.model.d.ts +0 -9
  342. package/services/docx/src/lib/docx.service.d.ts +0 -14
  343. package/services/docx/src/lib/pandoc-core.d.ts +0 -11
  344. package/services/docx/src/public-api.d.ts +0 -2
  345. package/services/license/index.d.ts +0 -1
  346. package/services/license/src/license.service.d.ts +0 -9
  347. package/utilities/extensions/src/array.extension.d.ts +0 -1
  348. package/utilities/extensions/src/color.extension.d.ts +0 -5
  349. package/utilities/extensions/src/date.extension.d.ts +0 -1
  350. package/utilities/extensions/src/number.extension.d.ts +0 -1
  351. package/utilities/extensions/src/string.extension.d.ts +0 -50
  352. package/utilities/models/src/color.model.d.ts +0 -4
  353. package/utilities/models/src/empty.model.d.ts +0 -4
  354. package/utilities/models/src/filter.model.d.ts +0 -12
  355. package/utilities/models/src/maybe-async.model.d.ts +0 -10
  356. package/utilities/models/src/nested-key-of.model.d.ts +0 -4
  357. package/utilities/models/src/operator.model.d.ts +0 -13
  358. package/utilities/models/src/order.model.d.ts +0 -4
  359. package/utilities/models/src/paging.model.d.ts +0 -8
  360. package/utilities/models/src/pattern.model.d.ts +0 -21
  361. package/utilities/models/src/size.model.d.ts +0 -4
@@ -1,6 +1,7 @@
1
1
  import { NgTemplateOutlet } from '@angular/common';
2
2
  import * as i0 from '@angular/core';
3
- import { input, booleanAttribute, model, signal, computed, effect, untracked, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { inject, input, booleanAttribute, model, signal, computed, effect, untracked, ChangeDetectionStrategy, Component } from '@angular/core';
4
+ import { I18nService } from '@sdcorejs/angular/i18n';
4
5
  import { SdDate } from '@sdcorejs/angular/forms/date';
5
6
  import { SdDatetime } from '@sdcorejs/angular/forms/datetime';
6
7
  import { SdInput } from '@sdcorejs/angular/forms/input';
@@ -11,8 +12,8 @@ import { SdOperator } from '@sdcorejs/angular/components/operator';
11
12
  import { FilterUtilities } from '@sdcorejs/utils/fns';
12
13
  import { SdIcon } from '@sdcorejs/angular/modules/icon';
13
14
 
14
- /** Type guard — narrows a `QbNode` to a `QbGroup`. */
15
- function isQbGroup(node) {
15
+ /** Type guard — narrows a `SdQbNode` to a `SdQbGroup`. */
16
+ function sdIsQbGroup(node) {
16
17
  return node.kind === 'group';
17
18
  }
18
19
  // ---------------------------------------------------------------------------
@@ -21,7 +22,7 @@ function isQbGroup(node) {
21
22
  // local so query-builder has no dependency on the query-bar package.
22
23
  // ---------------------------------------------------------------------------
23
24
  /** Allowed operators per field type — used when `field.operators` is not set. */
24
- const QB_OPERATORS_BY_TYPE = {
25
+ const SD_QB_OPERATORS_BY_TYPE = {
25
26
  string: ['CONTAIN', 'NOT_CONTAIN', 'EQUAL', 'NOT_EQUAL', 'START_WITH', 'END_WITH', 'NULL', 'NOT_NULL'],
26
27
  number: ['EQUAL', 'NOT_EQUAL', 'GREATER_THAN', 'GREATER_OR_EQUAL', 'LESS_THAN', 'LESS_OR_EQUAL', 'BETWEEN', 'NULL', 'NOT_NULL'],
27
28
  boolean: ['EQUAL', 'NOT_EQUAL'],
@@ -30,7 +31,7 @@ const QB_OPERATORS_BY_TYPE = {
30
31
  values: ['IN', 'NOT_IN', 'EQUAL', 'NOT_EQUAL', 'NULL', 'NOT_NULL'],
31
32
  };
32
33
  /** Default operator when a rule is first created for a field. */
33
- const QB_DEFAULT_OPERATOR_BY_TYPE = {
34
+ const SD_QB_DEFAULT_OPERATOR_BY_TYPE = {
34
35
  string: 'CONTAIN',
35
36
  number: 'EQUAL',
36
37
  boolean: 'EQUAL',
@@ -39,45 +40,45 @@ const QB_DEFAULT_OPERATOR_BY_TYPE = {
39
40
  values: 'IN',
40
41
  };
41
42
  /** Operators with no value payload — the value editor is hidden for these. */
42
- const QB_NO_DATA_OPERATORS = ['NULL', 'NOT_NULL'];
43
+ const SD_QB_NO_DATA_OPERATORS = ['NULL', 'NOT_NULL'];
43
44
  /** Operators whose value is an array — the value editor offers multi-select. */
44
- const QB_MULTI_OPERATORS = ['IN', 'NOT_IN'];
45
+ const SD_QB_MULTI_OPERATORS = ['IN', 'NOT_IN'];
45
46
  /** Operators allowed for `field` — explicit override wins, else the per-type set. */
46
- function qbAllowedOperators(field) {
47
+ function sdQbAllowedOperators(field) {
47
48
  if (!field)
48
49
  return [];
49
50
  if (field.operators?.length)
50
51
  return field.operators;
51
- return QB_OPERATORS_BY_TYPE[field.type] ?? [];
52
+ return SD_QB_OPERATORS_BY_TYPE[field.type] ?? [];
52
53
  }
53
54
  /** Starting operator for a new rule on `field` — falls back to the per-type default. */
54
- function qbDefaultOperator(field) {
55
+ function sdQbDefaultOperator(field) {
55
56
  if (!field)
56
57
  return undefined;
57
- const allowed = qbAllowedOperators(field);
58
+ const allowed = sdQbAllowedOperators(field);
58
59
  if (field.defaultOperator && allowed.includes(field.defaultOperator))
59
60
  return field.defaultOperator;
60
- const byType = QB_DEFAULT_OPERATOR_BY_TYPE[field.type];
61
+ const byType = SD_QB_DEFAULT_OPERATOR_BY_TYPE[field.type];
61
62
  return allowed.includes(byType) ? byType : allowed[0];
62
63
  }
63
64
  /** True when `op` is NULL / NOT_NULL (no value editor). */
64
- function qbIsNoDataOperator(op) {
65
- return !!op && QB_NO_DATA_OPERATORS.includes(op);
65
+ function sdQbIsNoDataOperator(op) {
66
+ return !!op && SD_QB_NO_DATA_OPERATORS.includes(op);
66
67
  }
67
68
  /** True when `op` is IN / NOT_IN (array value). */
68
- function qbIsMultiOperator(op) {
69
- return !!op && QB_MULTI_OPERATORS.includes(op);
69
+ function sdQbIsMultiOperator(op) {
70
+ return !!op && SD_QB_MULTI_OPERATORS.includes(op);
70
71
  }
71
72
  /** True when the operator accepts one right-hand operand, including a field reference. */
72
- function qbSupportsFieldCompareOperator(op) {
73
- return !!op && !qbIsNoDataOperator(op) && !qbIsMultiOperator(op) && op !== 'BETWEEN';
73
+ function sdQbSupportsFieldCompareOperator(op) {
74
+ return !!op && !sdQbIsNoDataOperator(op) && !sdQbIsMultiOperator(op) && op !== 'BETWEEN';
74
75
  }
75
76
  /**
76
77
  * Icon fallback per field type when `SdQueryBuilderField.icon` is not set.
77
78
  * Mirrors query-bar's `SD_QUERY_TYPE_ICON` so the two components read consistently,
78
79
  * but kept local so query-builder has no dependency on the query-bar package.
79
80
  */
80
- const QB_TYPE_ICON = {
81
+ const SD_QB_TYPE_ICON = {
81
82
  string: 'text_fields',
82
83
  number: 'tag',
83
84
  boolean: 'toggle_on',
@@ -87,12 +88,12 @@ const QB_TYPE_ICON = {
87
88
  };
88
89
  /**
89
90
  * Icon shown before a field in the field picker / selected trigger.
90
- * Priority: `field.icon` → `QB_TYPE_ICON[field.type]` → `'tune'` (default, like query-bar).
91
+ * Priority: `field.icon` → `SD_QB_TYPE_ICON[field.type]` → `'tune'` (default, like query-bar).
91
92
  */
92
- function qbFieldIcon(field) {
93
+ function sdQbFieldIcon(field) {
93
94
  if (!field)
94
95
  return 'tune';
95
- return field.icon ?? QB_TYPE_ICON[field.type] ?? 'tune';
96
+ return field.icon ?? SD_QB_TYPE_ICON[field.type] ?? 'tune';
96
97
  }
97
98
  // ---------------------------------------------------------------------------
98
99
  // Node factories — monotonic counter for stable, render-safe ids (no Math.random
@@ -100,52 +101,58 @@ function qbFieldIcon(field) {
100
101
  // ---------------------------------------------------------------------------
101
102
  let qbSeq = 0;
102
103
  /** Next stable node id with the given prefix. */
103
- function qbId(prefix = 'qb') {
104
+ function sdQbId(prefix = 'qb') {
104
105
  return `${prefix}-${++qbSeq}`;
105
106
  }
106
107
  /** Build a fresh rule node (optionally pre-filled). */
107
- function qbNewRule(field, operator, value, valueSource, compareField) {
108
- return { id: qbId('r'), kind: 'rule', field, operator, value, valueSource, compareField };
108
+ function sdQbNewRule(field, operator, value, valueSource, compareField) {
109
+ return { id: sdQbId('r'), kind: 'rule', field, operator, value, valueSource, compareField };
109
110
  }
110
111
  /** Build a fresh group node. */
111
- function qbNewGroup(logic = 'AND', children = []) {
112
- return { id: qbId('g'), kind: 'group', logic, children };
112
+ function sdQbNewGroup(logic = 'AND', children = []) {
113
+ return { id: sdQbId('g'), kind: 'group', logic, children };
113
114
  }
114
115
  /** Sentinel held as a rule value (and emitted as `data`) for the "today" date mode. */
115
- const QB_TODAY = 'TODAY';
116
+ const SD_QB_TODAY = 'TODAY';
116
117
  /** Type guard — narrows a rule value to a utils `DateRelative` (offset spec). */
117
- const qbIsRelativeDate = FilterUtilities.isDateRelative;
118
+ const sdQbIsRelativeDate = FilterUtilities.isDateRelative;
118
119
  /** Type guard — narrows a rule value to the `'TODAY'` sentinel ("now"/today mode). */
119
- function qbIsToday(v) {
120
- return v === QB_TODAY;
120
+ function sdQbIsToday(v) {
121
+ return v === SD_QB_TODAY;
121
122
  }
122
123
  /** Starting relative value when a rule first switches to "relative" mode. */
123
- function qbDefaultRelative() {
124
+ function sdQbDefaultRelative() {
124
125
  return { amount: 1, direction: 'previous', unit: 'day' };
125
126
  }
126
127
  /** Stable option list for the date-mode select (module ref — never reallocated). */
127
- const QB_DATE_MODES = [
128
- { value: 'absolute', display: 'Ngày cụ thể', icon: 'event' },
129
- { value: 'now', display: 'Hôm nay', icon: 'today' },
130
- { value: 'relative', display: 'Tương đối', icon: 'history' },
128
+ const SD_QB_DATE_MODES = [
129
+ { value: 'absolute', labelKey: 'core.component.query-builder.date-mode.absolute', icon: 'event' },
130
+ { value: 'now', labelKey: 'core.component.query-builder.date-mode.now', icon: 'today' },
131
+ { value: 'relative', labelKey: 'core.component.query-builder.date-mode.relative', icon: 'history' },
131
132
  ];
132
133
  /** Stable option list for choosing between a literal value and another field. */
133
- const QB_VALUE_SOURCE_OPTIONS = [
134
- { value: 'literal', display: 'Nhập giá trị', icon: 'edit_note' },
135
- { value: 'field', display: 'Chọn trường', icon: 'view_column' },
134
+ const SD_QB_VALUE_SOURCE_OPTIONS = [
135
+ { value: 'literal', labelKey: 'core.component.query-builder.value-source.literal', icon: 'edit_note' },
136
+ { value: 'field', labelKey: 'core.component.query-builder.value-source.field', icon: 'view_column' },
136
137
  ];
137
138
  /** Stable combined direction×unit option list (token `'unit:direction'`). */
138
- const QB_RELATIVE_UNIT_OPTIONS = [
139
- { value: 'day:previous', display: 'ngày trước' },
140
- { value: 'day:next', display: 'ngày tới' },
141
- { value: 'week:previous', display: 'tuần trước' },
142
- { value: 'week:next', display: 'tuần tới' },
143
- { value: 'month:previous', display: 'tháng trước' },
144
- { value: 'month:next', display: 'tháng tới' },
139
+ const SD_QB_RELATIVE_UNIT_OPTIONS = [
140
+ { value: 'day:previous', labelKey: 'core.component.query-builder.relative.day-previous' },
141
+ { value: 'day:next', labelKey: 'core.component.query-builder.relative.day-next' },
142
+ { value: 'week:previous', labelKey: 'core.component.query-builder.relative.week-previous' },
143
+ { value: 'week:next', labelKey: 'core.component.query-builder.relative.week-next' },
144
+ { value: 'month:previous', labelKey: 'core.component.query-builder.relative.month-previous' },
145
+ { value: 'month:next', labelKey: 'core.component.query-builder.relative.month-next' },
145
146
  ];
147
+ /** i18n key of the combined `unit`×`direction` phrase for a relative-date offset. */
148
+ function qbRelativeLabelKey(unit, direction) {
149
+ return `core.component.query-builder.relative.${unit}-${direction}`;
150
+ }
146
151
  /** Shared empty option array — stable ref for fallbacks (avoids per-call allocation). */
147
- const QB_EMPTY_OPTIONS = [];
152
+ const SD_QB_EMPTY_OPTIONS = [];
148
153
 
154
+ /** Fallback khi caller không truyền translator: trả về chính key để thiếu i18n lộ ra, không im lặng. */
155
+ const IDENTITY_TRANSLATE = key => key;
149
156
  // SQL symbol for each single-value comparison operator.
150
157
  const SYMBOL = {
151
158
  EQUAL: '=',
@@ -170,11 +177,11 @@ function ruleToFilter(rule) {
170
177
  if (!rule.field || !rule.operator)
171
178
  return null;
172
179
  const operator = rule.operator;
173
- if (QB_NO_DATA_OPERATORS.includes(operator)) {
180
+ if (SD_QB_NO_DATA_OPERATORS.includes(operator)) {
174
181
  return { field: rule.field, operator };
175
182
  }
176
183
  if (rule.valueSource === 'field') {
177
- if (!qbSupportsFieldCompareOperator(operator) || isEmptyValue(rule.compareField))
184
+ if (!sdQbSupportsFieldCompareOperator(operator) || isEmptyValue(rule.compareField))
178
185
  return null;
179
186
  return { field: rule.field, operator, dataType: 'field', data: rule.compareField };
180
187
  }
@@ -186,16 +193,16 @@ function ruleToFilter(rule) {
186
193
  return null;
187
194
  return { field: rule.field, operator: 'BETWEEN', data: { from: v.from, to: v.to } };
188
195
  }
189
- if (QB_MULTI_OPERATORS.includes(operator)) {
196
+ if (SD_QB_MULTI_OPERATORS.includes(operator)) {
190
197
  const arr = Array.isArray(rule.value) ? rule.value : isEmptyValue(rule.value) ? [] : [rule.value];
191
198
  if (!arr.length)
192
199
  return null;
193
200
  return { field: rule.field, operator, data: arr };
194
201
  }
195
- if (qbIsToday(rule.value)) {
196
- return { field: rule.field, operator, dataType: 'date-today', data: QB_TODAY };
202
+ if (sdQbIsToday(rule.value)) {
203
+ return { field: rule.field, operator, dataType: 'date-today', data: SD_QB_TODAY };
197
204
  }
198
- if (qbIsRelativeDate(rule.value)) {
205
+ if (sdQbIsRelativeDate(rule.value)) {
199
206
  const v = rule.value;
200
207
  // emit only when fully specified, else drop the incomplete rule
201
208
  if (v.unit && v.direction && typeof v.amount === 'number' && v.amount >= 1) {
@@ -216,7 +223,7 @@ function ruleToFilter(rule) {
216
223
  return { field: rule.field, operator, data: rule.value };
217
224
  }
218
225
  function nodeToFilter(node) {
219
- if (isQbGroup(node)) {
226
+ if (sdIsQbGroup(node)) {
220
227
  const data = node.children.map(nodeToFilter).filter((f) => f != null);
221
228
  if (!data.length)
222
229
  return null;
@@ -240,33 +247,33 @@ function treeToFilter(group) {
240
247
  // ---------------------------------------------------------------------------
241
248
  function ruleFromFilter(filter) {
242
249
  const operator = filter.operator;
243
- if (QB_NO_DATA_OPERATORS.includes(operator)) {
244
- return qbNewRule(filter.field, operator);
250
+ if (SD_QB_NO_DATA_OPERATORS.includes(operator)) {
251
+ return sdQbNewRule(filter.field, operator);
245
252
  }
246
253
  if (filter.dataType === 'field') {
247
- return qbNewRule(filter.field, operator, undefined, 'field', filter.data);
254
+ return sdQbNewRule(filter.field, operator, undefined, 'field', filter.data);
248
255
  }
249
256
  // Relative dates: read the utils `dataType` discriminator back into the internal value.
250
257
  if (filter.dataType === 'date-today') {
251
- return qbNewRule(filter.field, operator, QB_TODAY);
258
+ return sdQbNewRule(filter.field, operator, SD_QB_TODAY);
252
259
  }
253
260
  if (filter.dataType === 'date-relative') {
254
- return qbNewRule(filter.field, operator, filter.data);
261
+ return sdQbNewRule(filter.field, operator, filter.data);
255
262
  }
256
263
  // Back-compat: legacy `{ rel: 'now' | 'offset' }` payloads (pre-dataType migration)
257
264
  // still seed the editor correctly.
258
265
  const legacy = readLegacyRelative(filter.data);
259
266
  if (legacy !== undefined) {
260
- return qbNewRule(filter.field, operator, legacy);
267
+ return sdQbNewRule(filter.field, operator, legacy);
261
268
  }
262
- return qbNewRule(filter.field, operator, filter.data);
269
+ return sdQbNewRule(filter.field, operator, filter.data);
263
270
  }
264
271
  /** Map a pre-migration `{ rel }` payload to the new internal value, or `undefined` if not legacy. */
265
272
  function readLegacyRelative(data) {
266
273
  if (!data || typeof data !== 'object')
267
274
  return undefined;
268
275
  if (data.rel === 'now')
269
- return QB_TODAY;
276
+ return SD_QB_TODAY;
270
277
  if (data.rel === 'offset' && data.unit && data.direction && typeof data.amount === 'number') {
271
278
  return { amount: data.amount, direction: data.direction, unit: data.unit };
272
279
  }
@@ -276,7 +283,7 @@ function nodeFromFilter(filter) {
276
283
  return isAndOr$1(filter) ? groupFromFilter(filter) : ruleFromFilter(filter);
277
284
  }
278
285
  function groupFromFilter(filter) {
279
- return qbNewGroup(filter.operator, filter.data.map(nodeFromFilter));
286
+ return sdQbNewGroup(filter.operator, filter.data.map(nodeFromFilter));
280
287
  }
281
288
  /**
282
289
  * Inverse of {@link treeToFilter} — rebuild the editable tree from a `Filter` so a
@@ -284,14 +291,14 @@ function groupFromFilter(filter) {
284
291
  * `null` yields an empty AND root.
285
292
  *
286
293
  * @param filter - a `Filter` (group or leaf), or `null` / `undefined` for an empty tree.
287
- * @returns a fresh root `QbGroup` mirroring the filter's structure.
294
+ * @returns a fresh root `SdQbGroup` mirroring the filter's structure.
288
295
  */
289
296
  function filterToTree(filter) {
290
297
  if (!filter)
291
- return qbNewGroup('AND', []);
298
+ return sdQbNewGroup('AND', []);
292
299
  if (isAndOr$1(filter))
293
300
  return groupFromFilter(filter);
294
- return qbNewGroup('AND', [ruleFromFilter(filter)]);
301
+ return sdQbNewGroup('AND', [ruleFromFilter(filter)]);
295
302
  }
296
303
  // ---------------------------------------------------------------------------
297
304
  // Public Filter → SQL-ish highlight tokens (view mode)
@@ -299,23 +306,27 @@ function filterToTree(filter) {
299
306
  function escapeStr(s) {
300
307
  return s.replace(/'/g, "''");
301
308
  }
302
- const REL_UNIT_VI = { hour: 'giờ', day: 'ngày', week: 'tuần', month: 'tháng' };
303
- /** Render a relative-date offset as readable Vietnamese for the view string. */
304
- function formatRelative(v) {
305
- const unit = REL_UNIT_VI[v.unit] ?? REL_UNIT_VI.day;
306
- const dir = v.direction === 'next' ? 'tới' : 'trước';
307
- return `${v.amount} ${unit} ${dir}`;
309
+ /** Render a relative-date offset as a readable phrase in the active language. */
310
+ function formatRelative(v, t) {
311
+ // why: mỗi ngôn ngữ ghép `amount` với cụm đơn vị+hướng theo trật tự khác nhau ('3 ngày trước' /
312
+ // '3 days ago' / '3日前'), nên để catalog quyết định trật tự thay vì nối chuỗi cứng ở đây.
313
+ return t('core.component.query-builder.relative.format', {
314
+ amount: v.amount,
315
+ phrase: t(qbRelativeLabelKey(v.unit, v.direction)),
316
+ });
308
317
  }
309
318
  /** Format one scalar value for the view string — quoted/labelled per field type. */
310
- function formatScalar(field, raw) {
311
- if (qbIsToday(raw))
312
- return 'hôm nay';
313
- if (qbIsRelativeDate(raw))
314
- return formatRelative(raw);
319
+ function formatScalar(field, raw, t) {
320
+ if (sdQbIsToday(raw))
321
+ return t('core.component.query-builder.date-mode.now');
322
+ if (sdQbIsRelativeDate(raw))
323
+ return formatRelative(raw, t);
315
324
  if (raw === null || raw === undefined)
316
325
  return '';
317
326
  if (field?.type === 'boolean') {
318
- return raw ? (field.trueLabel ?? 'Có') : (field.falseLabel ?? 'Không');
327
+ return raw
328
+ ? (field.trueLabel ?? t('core.component.query-builder.boolean.true'))
329
+ : (field.falseLabel ?? t('core.component.query-builder.boolean.false'));
319
330
  }
320
331
  if (field?.type === 'values') {
321
332
  const opt = field.values?.find(o => o.value === raw);
@@ -340,12 +351,12 @@ function operatorToken(operator) {
340
351
  return operator.startsWith('NOT_') ? 'not like' : 'like';
341
352
  return SYMBOL[operator] ?? operator;
342
353
  }
343
- function ruleTokens(filter, fields) {
354
+ function ruleTokens(filter, fields, t) {
344
355
  const field = fields.find(f => f.key === filter.field);
345
356
  const out = [{ kind: 'field', text: field?.label ?? String(filter.field) }];
346
357
  const sp = () => out.push({ kind: 'plain', text: ' ' });
347
358
  const operator = filter.operator;
348
- if (QB_NO_DATA_OPERATORS.includes(operator)) {
359
+ if (SD_QB_NO_DATA_OPERATORS.includes(operator)) {
349
360
  sp();
350
361
  out.push({ kind: 'op', text: operator === 'NULL' ? 'is null' : 'is not null' });
351
362
  return out;
@@ -354,17 +365,17 @@ function ruleTokens(filter, fields) {
354
365
  sp();
355
366
  out.push({ kind: 'op', text: 'between' });
356
367
  sp();
357
- out.push({ kind: 'value', text: formatScalar(field, filter.data?.from) });
368
+ out.push({ kind: 'value', text: formatScalar(field, filter.data?.from, t) });
358
369
  out.push({ kind: 'plain', text: ' and ' });
359
- out.push({ kind: 'value', text: formatScalar(field, filter.data?.to) });
370
+ out.push({ kind: 'value', text: formatScalar(field, filter.data?.to, t) });
360
371
  return out;
361
372
  }
362
- if (QB_MULTI_OPERATORS.includes(operator)) {
373
+ if (SD_QB_MULTI_OPERATORS.includes(operator)) {
363
374
  sp();
364
375
  out.push({ kind: 'op', text: operator === 'IN' ? 'in' : 'not in' });
365
376
  sp();
366
377
  const arr = Array.isArray(filter.data) ? filter.data : [];
367
- out.push({ kind: 'value', text: `(${arr.map((v) => formatScalar(field, v)).join(', ')})` });
378
+ out.push({ kind: 'value', text: `(${arr.map((v) => formatScalar(field, v, t)).join(', ')})` });
368
379
  return out;
369
380
  }
370
381
  if (filter.dataType === 'field') {
@@ -385,16 +396,16 @@ function ruleTokens(filter, fields) {
385
396
  sp();
386
397
  out.push({ kind: 'op', text: operatorToken(operator) });
387
398
  sp();
388
- out.push({ kind: 'value', text: formatScalar(field, filter.data) });
399
+ out.push({ kind: 'value', text: formatScalar(field, filter.data, t) });
389
400
  return out;
390
401
  }
391
- function tokensFor(filter, fields) {
402
+ function tokensFor(filter, fields, t) {
392
403
  if (isAndOr$1(filter)) {
393
404
  const out = [];
394
405
  filter.data.forEach((child, i) => {
395
406
  if (i > 0)
396
407
  out.push({ kind: 'logic', text: ` ${filter.operator.toLowerCase()} ` });
397
- const childTokens = tokensFor(child, fields);
408
+ const childTokens = tokensFor(child, fields, t);
398
409
  // Wrap a nested multi-child group in parentheses; a single-child group needs none.
399
410
  if (isAndOr$1(child) && child.data.length > 1) {
400
411
  out.push({ kind: 'paren', text: '(' }, ...childTokens, { kind: 'paren', text: ')' });
@@ -405,23 +416,26 @@ function tokensFor(filter, fields) {
405
416
  });
406
417
  return out;
407
418
  }
408
- return ruleTokens(filter, fields);
419
+ return ruleTokens(filter, fields, t);
409
420
  }
410
421
  /**
411
422
  * Render a `Filter` to a SQL-ish token stream for view mode. Field tokens use the
412
423
  * matching field's `label`; operators map to SQL syntax (`= != like between is null …`);
413
424
  * `and` / `or` are lowercase; nested multi-child groups are wrapped in parentheses.
414
425
  * Returns a token stream (not a string) so the template can wrap each piece in a
415
- * highlight `<span>` keyed by `QbToken.kind`.
426
+ * highlight `<span>` keyed by `SdQbToken.kind`.
416
427
  *
417
428
  * @param filter - the `Filter` to render, or `null` / `undefined`.
418
429
  * @param fields - field metadata, used to resolve each field key to its display label + value formatting.
430
+ * @param translate - i18n lookup for language-dependent value tokens (today / relative offsets /
431
+ * boolean labels). Optional so the serializer stays callable outside an Angular injection context;
432
+ * omitting it renders the raw i18n keys.
419
433
  * @returns the ordered tokens, or `[]` when `filter` is empty.
420
434
  */
421
- function filterToTokens(filter, fields) {
435
+ function filterToTokens(filter, fields, translate = IDENTITY_TRANSLATE) {
422
436
  if (!filter)
423
437
  return [];
424
- return tokensFor(filter, fields);
438
+ return tokensFor(filter, fields, translate);
425
439
  }
426
440
 
427
441
  /** A logical `Filter` (AND / OR group) carries a `data[]` array. */
@@ -437,6 +451,7 @@ const QB_EMPTY_FIELDS = [];
437
451
  * string with highlighted operators + values.
438
452
  */
439
453
  class SdQueryBuilder {
454
+ #i18n = inject(I18nService);
440
455
  // -------------------------------------------------------------------------
441
456
  // Inputs
442
457
  // -------------------------------------------------------------------------
@@ -464,7 +479,7 @@ class SdQueryBuilder {
464
479
  // -------------------------------------------------------------------------
465
480
  // Internal source of truth — the editable tree (carries UI state).
466
481
  // -------------------------------------------------------------------------
467
- #tree = signal(qbNewGroup('AND'));
482
+ #tree = signal(sdQbNewGroup('AND'));
468
483
  tree = this.#tree.asReadonly();
469
484
  /** Last filter WE emitted — used to recognise (and ignore) our own echo in the inbound effect. */
470
485
  #lastEmitted = undefined;
@@ -491,19 +506,40 @@ class SdQueryBuilder {
491
506
  for (const f of this.resolvedFields()) {
492
507
  if (f.type === 'boolean') {
493
508
  map.set(f.key, [
494
- { value: true, display: f.trueLabel ?? '' },
495
- { value: false, display: f.falseLabel ?? 'Không' },
509
+ { value: true, display: f.trueLabel ?? this.#i18n.t('core.component.query-builder.boolean.true') },
510
+ { value: false, display: f.falseLabel ?? this.#i18n.t('core.component.query-builder.boolean.false') },
496
511
  ]);
497
512
  }
498
513
  }
499
514
  return map;
500
515
  });
516
+ // -------------------------------------------------------------------------
517
+ // Option lists — dịch lúc ĐỌC, không phải lúc module được đánh giá.
518
+ //
519
+ // why: bảng hằng số trong `query-builder.model.ts` chỉ giữ `labelKey`; `computed` ở đây mới sinh
520
+ // ra `display`. Nhờ vậy đổi ngôn ngữ là nhãn đổi theo (computed phụ thuộc signal `messages()` bên
521
+ // trong `I18nService.t`), mà tham chiếu mảng vẫn ỔN ĐỊNH giữa các chu kỳ change detection vì
522
+ // computed chỉ tính lại khi ngôn ngữ đổi — bắt buộc, nếu không `sd-select` sẽ rơi lại vào vòng lặp
523
+ // `toObservable(items)` → `markForCheck()` → CD mới → mảng mới (xem `#booleanOptionsByKey`).
524
+ // -------------------------------------------------------------------------
501
525
  /** Stable option list for the date-mode select. */
502
- dateModes = QB_DATE_MODES;
526
+ dateModes = computed(() => SD_QB_DATE_MODES.map(o => ({ ...o, display: this.#i18n.t(o.labelKey) })));
503
527
  /** Stable option list for the literal-vs-field operand select. */
504
- valueSourceOptions = QB_VALUE_SOURCE_OPTIONS;
528
+ valueSourceOptions = computed(() => SD_QB_VALUE_SOURCE_OPTIONS.map(o => ({ ...o, display: this.#i18n.t(o.labelKey) })));
505
529
  /** Stable combined unit×direction option list for the relative select. */
506
- relativeUnitOptions = QB_RELATIVE_UNIT_OPTIONS;
530
+ relativeUnitOptions = computed(() => SD_QB_RELATIVE_UNIT_OPTIONS.map(o => ({ ...o, display: this.#i18n.t(o.labelKey) })));
531
+ // Nhãn i18n tĩnh của template (placeholder / title / aria-label / trạng thái rỗng).
532
+ selectFieldLabel = computed(() => this.#i18n.t('core.component.query-builder.select-field'));
533
+ valueLabel = computed(() => this.#i18n.t('core.component.query-builder.value'));
534
+ fromLabel = computed(() => this.#i18n.t('core.component.query-builder.from'));
535
+ toLabel = computed(() => this.#i18n.t('core.component.query-builder.to'));
536
+ addLabel = computed(() => this.#i18n.t('core.component.query-builder.add'));
537
+ addNodeLabel = computed(() => this.#i18n.t('core.component.query-builder.add-node'));
538
+ conditionLabel = computed(() => this.#i18n.t('core.component.query-builder.condition'));
539
+ groupLabel = computed(() => this.#i18n.t('core.component.query-builder.group'));
540
+ removeGroupLabel = computed(() => this.#i18n.t('core.component.query-builder.remove-group'));
541
+ removeConditionLabel = computed(() => this.#i18n.t('core.component.query-builder.remove-condition'));
542
+ emptyLabel = computed(() => this.#i18n.t('core.component.query-builder.empty'));
507
543
  #compareFieldsByKey = computed(() => {
508
544
  const fields = this.resolvedFields();
509
545
  const map = new Map();
@@ -526,13 +562,20 @@ class SdQueryBuilder {
526
562
  }
527
563
  return map;
528
564
  });
529
- /** View-mode token stream — recomputed from the emitted value. */
530
- tokens = computed(() => filterToTokens(this.option()?.value ?? this.value(), this.resolvedFields()));
565
+ /**
566
+ * Translator handed to the serializer.
567
+ * why: `filterToTokens` là hàm thuần cấp module, không inject được. Truyền hàm dịch vào để chuỗi
568
+ * view-mode ("hôm nay", "3 ngày trước", "Có"/"Không") không còn hardcode tiếng Việt. Trường ổn
569
+ * định (không tạo mới mỗi lần gọi) để `computed` bên dưới không phụ thuộc tham chiếu mới.
570
+ */
571
+ #translate = (key, params) => this.#i18n.t(key, params);
572
+ /** View-mode token stream — recomputed from the emitted value (and from the active language). */
573
+ tokens = computed(() => filterToTokens(this.option()?.value ?? this.value(), this.resolvedFields(), this.#translate));
531
574
  isView = computed(() => this.resolvedMode() === 'view');
532
575
  /** Expose the type-guard to the template. */
533
- isGroup = isQbGroup;
576
+ isGroup = sdIsQbGroup;
534
577
  /** Resolve the leading icon for a field (own `icon` → per-type → `'tune'`) — used by the field picker. */
535
- fieldIcon = qbFieldIcon;
578
+ fieldIcon = sdQbFieldIcon;
536
579
  constructor() {
537
580
  // Inbound seeding: an EXTERNAL write to value / filters rebuilds the tree.
538
581
  // Our own emits are recognised via #lastEmitted and skipped (no echo loop).
@@ -610,12 +653,12 @@ class SdQueryBuilder {
610
653
  this.#bumpTree();
611
654
  }
612
655
  #hasOpenDropdown(node) {
613
- if (!isQbGroup(node))
656
+ if (!sdIsQbGroup(node))
614
657
  return false;
615
658
  return node.open === true || node.children.some(c => this.#hasOpenDropdown(c));
616
659
  }
617
660
  #closeAllDropdowns(node) {
618
- if (isQbGroup(node)) {
661
+ if (sdIsQbGroup(node)) {
619
662
  node.open = false;
620
663
  node.children.forEach(c => this.#closeAllDropdowns(c));
621
664
  }
@@ -624,7 +667,7 @@ class SdQueryBuilder {
624
667
  addRule(group) {
625
668
  if (this.resolvedDisabled())
626
669
  return;
627
- group.children.push(qbNewRule());
670
+ group.children.push(sdQbNewRule());
628
671
  group.open = false;
629
672
  this.#apply();
630
673
  }
@@ -632,7 +675,7 @@ class SdQueryBuilder {
632
675
  addGroup(group) {
633
676
  if (this.resolvedDisabled())
634
677
  return;
635
- group.children.push(qbNewGroup('AND', [qbNewRule()]));
678
+ group.children.push(sdQbNewGroup('AND', [sdQbNewRule()]));
636
679
  group.open = false;
637
680
  this.#apply();
638
681
  }
@@ -661,7 +704,7 @@ class SdQueryBuilder {
661
704
  return; // swap-only: never clear the field (issue #2)
662
705
  rule.field = key;
663
706
  const field = this.fieldByKey()[rule.field];
664
- const op = qbDefaultOperator(field);
707
+ const op = sdQbDefaultOperator(field);
665
708
  rule.operator = op;
666
709
  rule.value = this.#defaultValueFor(op);
667
710
  this.#useLiteralSource(rule);
@@ -742,25 +785,25 @@ class SdQueryBuilder {
742
785
  return raw;
743
786
  }
744
787
  #defaultValueFor(op) {
745
- if (qbIsMultiOperator(op))
788
+ if (sdQbIsMultiOperator(op))
746
789
  return [];
747
790
  if (op === 'BETWEEN')
748
791
  return { from: null, to: null };
749
792
  return null;
750
793
  }
751
794
  #reshapeValue(op, current) {
752
- if (qbIsNoDataOperator(op))
795
+ if (sdQbIsNoDataOperator(op))
753
796
  return null;
754
- if (qbIsMultiOperator(op))
797
+ if (sdQbIsMultiOperator(op))
755
798
  return Array.isArray(current) ? current : current == null ? [] : [current];
756
799
  if (op === 'BETWEEN') {
757
800
  // a relative spec / 'TODAY' can't be a BETWEEN endpoint — reset to a fresh range
758
- return current && typeof current === 'object' && !Array.isArray(current) && !qbIsRelativeDate(current)
801
+ return current && typeof current === 'object' && !Array.isArray(current) && !sdQbIsRelativeDate(current)
759
802
  ? current
760
803
  : { from: null, to: null };
761
804
  }
762
805
  // single value — keep a relative spec or the 'TODAY' sentinel; else drop array/range remnants
763
- if (qbIsToday(current) || qbIsRelativeDate(current))
806
+ if (sdQbIsToday(current) || sdQbIsRelativeDate(current))
764
807
  return current;
765
808
  return Array.isArray(current) || (current && typeof current === 'object') ? null : current;
766
809
  }
@@ -780,7 +823,7 @@ class SdQueryBuilder {
780
823
  }
781
824
  /** Operators allowed for a rule, derived from its field's type / override. */
782
825
  allowedOperators(rule) {
783
- return qbAllowedOperators(this.fieldOf(rule));
826
+ return sdQbAllowedOperators(this.fieldOf(rule));
784
827
  }
785
828
  /** Whether to render the operator picker — hidden when only one operator is allowed. */
786
829
  showOperatorSelector(rule) {
@@ -793,7 +836,7 @@ class SdQueryBuilder {
793
836
  return this.#compareFieldsByKey().get(field.key) ?? QB_EMPTY_FIELDS;
794
837
  }
795
838
  canCompareWithField(rule) {
796
- return qbSupportsFieldCompareOperator(rule.operator) && this.compareFields(rule).length > 0;
839
+ return sdQbSupportsFieldCompareOperator(rule.operator) && this.compareFields(rule).length > 0;
797
840
  }
798
841
  showValueSourceSelector(rule) {
799
842
  return this.canCompareWithField(rule) && (this.resolvedComparisonMode() === 'value-or-field' || rule.valueSource === 'field');
@@ -806,22 +849,39 @@ class SdQueryBuilder {
806
849
  }
807
850
  /** True for NULL / NOT_NULL — the template then renders no value editor. */
808
851
  isNoData(op) {
809
- return qbIsNoDataOperator(op);
852
+ return sdQbIsNoDataOperator(op);
810
853
  }
811
854
  /** True for IN / NOT_IN — the template then renders a multi-select value editor. */
812
855
  isMulti(op) {
813
- return qbIsMultiOperator(op);
856
+ return sdQbIsMultiOperator(op);
814
857
  }
815
858
  /** `[true/false]` option list for a boolean field's value select (stable reference). */
816
859
  booleanItems(field) {
817
- return this.#booleanOptionsByKey().get(field.key) ?? QB_EMPTY_OPTIONS;
860
+ return this.#booleanOptionsByKey().get(field.key) ?? SD_QB_EMPTY_OPTIONS;
861
+ }
862
+ /**
863
+ * Option list cho field `type:'values'`.
864
+ *
865
+ * why: template cũ bind thẳng `[items]="fieldOf(rule)!.values || []"`. `values` là OPTIONAL trên
866
+ * `SdQueryBuilderField`, nên với field khai thiếu `values`, nhánh `|| []` cấp phát một mảng MỚI
867
+ * mỗi lần change detection chạy — đúng nguyên nhân OOM đã ghi ở `#booleanOptionsByKey`:
868
+ * `sd-select` có `toObservable(items)` → `markForCheck()` → CD mới → mảng mới → lặp vô hạn.
869
+ *
870
+ * why: KHÔNG memo hoá qua `computed<Map>` snapshot. `field.values` vốn ĐÃ là mảng của consumer
871
+ * nên tự nó ổn định; snapshot vào Map chỉ thêm một tầng cache không bao giờ invalidate khi
872
+ * consumer nạp option bất đồng bộ bằng cách gán tại chỗ (`fields[0].values = loaded`) — reference
873
+ * `fields` không đổi nên `computed` giữ nguyên list cũ vĩnh viễn. Chỉ nhánh RỖNG cần hằng số
874
+ * dùng chung để tránh literal mới mỗi CD.
875
+ */
876
+ valueItems(field) {
877
+ return field.values?.length ? field.values : SD_QB_EMPTY_OPTIONS;
818
878
  }
819
879
  /** Current date-value mode of a rule, derived from its value (no separate state). */
820
880
  dateMode(rule) {
821
881
  const v = rule.value;
822
- if (qbIsToday(v))
882
+ if (sdQbIsToday(v))
823
883
  return 'now';
824
- if (qbIsRelativeDate(v))
884
+ if (sdQbIsRelativeDate(v))
825
885
  return 'relative';
826
886
  return 'absolute';
827
887
  }
@@ -831,9 +891,9 @@ class SdQueryBuilder {
831
891
  return;
832
892
  this.#useLiteralSource(rule);
833
893
  if (mode === 'now')
834
- rule.value = QB_TODAY;
894
+ rule.value = SD_QB_TODAY;
835
895
  else if (mode === 'relative')
836
- rule.value = qbDefaultRelative();
896
+ rule.value = sdQbDefaultRelative();
837
897
  else
838
898
  rule.value = null; // absolute — pick a concrete date
839
899
  this.#apply();
@@ -841,7 +901,7 @@ class SdQueryBuilder {
841
901
  /** Read the offset amount of a relative rule (default 1). */
842
902
  relativeAmount(rule) {
843
903
  const v = rule.value;
844
- return qbIsRelativeDate(v) ? (v.amount ?? 1) : 1;
904
+ return sdQbIsRelativeDate(v) ? (v.amount ?? 1) : 1;
845
905
  }
846
906
  /** Set the offset amount (clamped to an integer >= 1). */
847
907
  setRelativeAmount(rule, raw) {
@@ -850,14 +910,14 @@ class SdQueryBuilder {
850
910
  this.#useLiteralSource(rule);
851
911
  const n = Math.floor(Number(raw));
852
912
  const amount = Number.isNaN(n) || n < 1 ? 1 : n;
853
- const cur = qbIsRelativeDate(rule.value) ? rule.value : qbDefaultRelative();
913
+ const cur = sdQbIsRelativeDate(rule.value) ? rule.value : sdQbDefaultRelative();
854
914
  rule.value = { amount, direction: cur.direction, unit: cur.unit };
855
915
  this.#apply();
856
916
  }
857
917
  /** Read the `'unit:direction'` token of a relative rule (default `'day:previous'`). */
858
918
  relativeUnitDirValue(rule) {
859
919
  const v = rule.value;
860
- if (qbIsRelativeDate(v))
920
+ if (sdQbIsRelativeDate(v))
861
921
  return `${v.unit}:${v.direction}`;
862
922
  return 'day:previous';
863
923
  }
@@ -868,7 +928,7 @@ class SdQueryBuilder {
868
928
  this.#useLiteralSource(rule);
869
929
  const normalizedToken = typeof token === 'string' ? token : 'day:previous';
870
930
  const [unit, direction] = normalizedToken.split(':');
871
- const cur = qbIsRelativeDate(rule.value) ? rule.value : qbDefaultRelative();
931
+ const cur = sdQbIsRelativeDate(rule.value) ? rule.value : sdQbDefaultRelative();
872
932
  rule.value = { amount: cur.amount, direction, unit };
873
933
  this.#apply();
874
934
  }
@@ -877,16 +937,16 @@ class SdQueryBuilder {
877
937
  return `${this.resolvedAutoId() ?? 'qb'}-${suffix}`;
878
938
  }
879
939
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdQueryBuilder, deps: [], target: i0.ɵɵFactoryTarget.Component });
880
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: SdQueryBuilder, isStandalone: true, selector: "sd-query-builder", inputs: { option: { classPropertyName: "option", publicName: "option", isSignal: true, isRequired: false, transformFunction: null }, fields: { classPropertyName: "fields", publicName: "fields", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, comparisonMode: { classPropertyName: "comparisonMode", publicName: "comparisonMode", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, autoId: { classPropertyName: "autoId", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, filters: { classPropertyName: "filters", publicName: "filters", isSignal: true, isRequired: false, transformFunction: null }, rootLogic: { classPropertyName: "rootLogic", publicName: "rootLogic", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", filters: "filtersChange", rootLogic: "rootLogicChange" }, ngImport: i0, template: "@if (isView()) {\n <div class=\"qb-view\" [attr.data-autoid]=\"autoIdFor('view')\" aria-readonly=\"true\">\n @if (tokens().length) {\n @for (t of tokens(); track $index) {\n <span class=\"qb-tok qb-tok-{{ t.kind }}\">{{ t.text }}</span>\n }\n } @else {\n <span class=\"qb-view-empty\">\u2014</span>\n }\n </div>\n} @else {\n <div class=\"qb-container\" [class.is-disabled]=\"resolvedDisabled()\" (click)=\"closeAllDropdowns()\">\n <ng-container [ngTemplateOutlet]=\"groupTpl\" [ngTemplateOutletContext]=\"{ $implicit: tree(), isRoot: true }\"></ng-container>\n </div>\n}\n\n<!-- Recursive group node -->\n<ng-template #groupTpl let-group let-isRoot=\"isRoot\" let-parent=\"parent\" let-index=\"index\">\n <div class=\"qb-group\" [class.is-root]=\"isRoot\">\n <div class=\"qb-header\" [class.is-open]=\"group.open\">\n <div class=\"qb-logic-switch\">\n <button type=\"button\" [class.active]=\"group.logic === 'AND'\" [disabled]=\"resolvedDisabled()\" (click)=\"setGroupLogic(group, 'AND')\">AND</button>\n <button type=\"button\" [class.active]=\"group.logic === 'OR'\" [disabled]=\"resolvedDisabled()\" (click)=\"setGroupLogic(group, 'OR')\">OR</button>\n </div>\n\n <div class=\"qb-actions\">\n <button type=\"button\" class=\"qb-btn qb-btn-add\" title=\"Th\u00EAm\" aria-label=\"Th\u00EAm \u0111i\u1EC1u ki\u1EC7n ho\u1EB7c nh\u00F3m\" [disabled]=\"resolvedDisabled()\" (click)=\"toggleDropdown(group, $event)\">+</button>\n @if (group.open) {\n <div class=\"qb-dropdown\" (click)=\"$event.stopPropagation()\">\n <button type=\"button\" (click)=\"addRule(group)\">+ \u0110i\u1EC1u ki\u1EC7n</button>\n <button type=\"button\" (click)=\"addGroup(group)\">+ Nh\u00F3m</button>\n </div>\n }\n @if (!isRoot) {\n <button type=\"button\" class=\"qb-btn qb-btn-remove\" title=\"Xo\u00E1 nh\u00F3m\" aria-label=\"Xo\u00E1 nh\u00F3m\" [disabled]=\"resolvedDisabled()\" (click)=\"removeNode(parent, index)\">\u2715</button>\n }\n </div>\n </div>\n\n <div class=\"qb-body\">\n @for (node of group.children; track node.id; let i = $index) {\n <div class=\"qb-treenode\" [class.is-group]=\"isGroup(node)\" [class.is-rule]=\"!isGroup(node)\">\n @if (isGroup(node)) {\n <ng-container\n [ngTemplateOutlet]=\"groupTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: node, isRoot: false, parent: group, index: i }\"\n ></ng-container>\n } @else {\n <ng-container [ngTemplateOutlet]=\"ruleTpl\" [ngTemplateOutletContext]=\"{ $implicit: node, parent: group, index: i }\"></ng-container>\n }\n </div>\n } @empty {\n <div class=\"qb-empty\">Ch\u01B0a c\u00F3 \u0111i\u1EC1u ki\u1EC7n</div>\n }\n </div>\n </div>\n</ng-template>\n\n<!-- Single rule node: field \u2192 operator \u2192 value -->\n<ng-template #ruleTpl let-rule let-parent=\"parent\" let-index=\"index\">\n <div class=\"qb-rule\">\n <sd-select\n class=\"qb-field\"\n [items]=\"resolvedFields()\"\n valueField=\"key\"\n displayField=\"label\"\n placeholder=\"Ch\u1ECDn tr\u01B0\u1EDDng\"\n size=\"sm\"\n [model]=\"rule.field\"\n (modelChange)=\"setField(rule, $event)\"\n [disabled]=\"resolvedDisabled()\"\n [clearable]=\"false\"\n hideInlineError\n [autoId]=\"autoIdFor('field-' + index)\"\n >\n <!-- Option trong dropdown: icon th\u1EC3 hi\u1EC7n type + label. -->\n <ng-template sdItemDef let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"fieldIcon($any(item))\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item).label }}</span>\n </span>\n </ng-template>\n <!-- Gi\u00E1 tr\u1ECB \u0111\u00E3 ch\u1ECDn \u1EDF trigger: icon m\u1EB7c \u0111\u1ECBnh theo type khi field ch\u01B0a c\u00F3 icon ri\u00EAng. -->\n <ng-template #sdSelected let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"fieldIcon($any(item))\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item)?.label }}</span>\n </span>\n </ng-template>\n </sd-select>\n\n @if (rule.field) {\n <div class=\"qb-divider\"></div>\n\n @if (showOperatorSelector(rule)) {\n <sd-operator\n class=\"qb-op\"\n [operators]=\"allowedOperators(rule)\"\n [model]=\"rule.operator\"\n (modelChange)=\"setOperator(rule, $event)\"\n [disabled]=\"resolvedDisabled()\"\n [autoId]=\"autoIdFor('op-' + index)\"\n ></sd-operator>\n }\n\n @if (rule.operator && !isNoData(rule.operator)) {\n <div class=\"qb-divider\"></div>\n\n @if (showValueSourceSelector(rule)) {\n <sd-select\n class=\"qb-value-source\"\n [items]=\"valueSourceOptions\"\n valueField=\"value\"\n displayField=\"display\"\n size=\"sm\"\n [model]=\"valueSourceOf(rule)\"\n (modelChange)=\"setValueSource(rule, $event)\"\n [disabled]=\"resolvedDisabled()\"\n [clearable]=\"false\"\n hideInlineError\n [autoId]=\"autoIdFor('valuesource-' + index)\"\n >\n <ng-template sdItemDef let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"$any(item).icon\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item).display }}</span>\n </span>\n </ng-template>\n <ng-template #sdSelected let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"$any(item)?.icon\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item)?.display }}</span>\n </span>\n </ng-template>\n </sd-select>\n <div class=\"qb-divider\"></div>\n }\n\n @if (isFieldSource(rule)) {\n <sd-select\n class=\"qb-val qb-compare-field\"\n [items]=\"compareFields(rule)\"\n valueField=\"key\"\n displayField=\"label\"\n placeholder=\"Ch\u1ECDn tr\u01B0\u1EDDng\"\n size=\"sm\"\n [model]=\"rule.compareField\"\n (modelChange)=\"setCompareField(rule, $event)\"\n [disabled]=\"resolvedDisabled()\"\n [clearable]=\"false\"\n hideInlineError\n [autoId]=\"autoIdFor('compare-field-' + index)\"\n >\n <ng-template sdItemDef let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"fieldIcon($any(item))\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item).label }}</span>\n </span>\n </ng-template>\n <ng-template #sdSelected let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"fieldIcon($any(item))\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item)?.label }}</span>\n </span>\n </ng-template>\n </sd-select>\n } @else if (rule.operator === 'BETWEEN') {\n <div class=\"qb-between\">\n @switch (fieldOf(rule)?.type) {\n @case ('number') {\n <sd-input-number class=\"qb-val\" placeholder=\"T\u1EEB\" size=\"sm\" [model]=\"rule.value?.from\" (modelChange)=\"setBetweenFrom(rule, $event)\" [min]=\"fieldOf(rule)?.min\" [max]=\"fieldOf(rule)?.max\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('from-' + index)\"></sd-input-number>\n <span class=\"qb-dash\">\u2014</span>\n <sd-input-number class=\"qb-val\" placeholder=\"\u0110\u1EBFn\" size=\"sm\" [model]=\"rule.value?.to\" (modelChange)=\"setBetweenTo(rule, $event)\" [min]=\"fieldOf(rule)?.min\" [max]=\"fieldOf(rule)?.max\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('to-' + index)\"></sd-input-number>\n }\n @case ('date') {\n <sd-date class=\"qb-val qb-date-val\" size=\"sm\" [model]=\"rule.value?.from\" (modelChange)=\"setBetweenFrom(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('from-' + index)\"></sd-date>\n <span class=\"qb-dash\">\u2014</span>\n <sd-date class=\"qb-val qb-date-val\" size=\"sm\" [model]=\"rule.value?.to\" (modelChange)=\"setBetweenTo(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('to-' + index)\"></sd-date>\n }\n @case ('datetime') {\n <sd-datetime class=\"qb-val\" size=\"sm\" [model]=\"rule.value?.from\" (modelChange)=\"setBetweenFrom(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('from-' + index)\"></sd-datetime>\n <span class=\"qb-dash\">\u2014</span>\n <sd-datetime class=\"qb-val\" size=\"sm\" [model]=\"rule.value?.to\" (modelChange)=\"setBetweenTo(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('to-' + index)\"></sd-datetime>\n }\n }\n </div>\n } @else {\n @switch (fieldOf(rule)?.type) {\n @case ('string') {\n <sd-input class=\"qb-val\" placeholder=\"Gi\u00E1 tr\u1ECB\" size=\"sm\" [model]=\"rule.value\" (modelChange)=\"setScalar(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('val-' + index)\"></sd-input>\n }\n @case ('number') {\n <sd-input-number class=\"qb-val\" placeholder=\"Gi\u00E1 tr\u1ECB\" size=\"sm\" [model]=\"rule.value\" (modelChange)=\"setScalar(rule, $event)\" [min]=\"fieldOf(rule)?.min\" [max]=\"fieldOf(rule)?.max\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('val-' + index)\"></sd-input-number>\n }\n @case ('boolean') {\n <sd-select class=\"qb-val\" [items]=\"booleanItems(fieldOf(rule)!)\" valueField=\"value\" displayField=\"display\" placeholder=\"Gi\u00E1 tr\u1ECB\" size=\"sm\" [model]=\"rule.value\" (modelChange)=\"setScalar(rule, $event)\" [disabled]=\"resolvedDisabled()\" [clearable]=\"false\" hideInlineError [autoId]=\"autoIdFor('val-' + index)\"></sd-select>\n }\n @case ('date') {\n @let _dateMode = dateMode(rule);\n <sd-select class=\"qb-date-mode\" [items]=\"dateModes\" valueField=\"value\" displayField=\"display\" size=\"sm\" [model]=\"_dateMode\" (modelChange)=\"setDateMode(rule, $event)\" [disabled]=\"resolvedDisabled()\" [clearable]=\"false\" hideInlineError [autoId]=\"autoIdFor('datemode-' + index)\">\n <ng-template sdItemDef let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"$any(item).icon\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item).display }}</span>\n </span>\n </ng-template>\n <ng-template #sdSelected let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"$any(item)?.icon\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item)?.display }}</span>\n </span>\n </ng-template>\n </sd-select>\n @switch (_dateMode) {\n @case ('absolute') {\n <sd-date class=\"qb-val qb-date-val\" size=\"sm\" [model]=\"rule.value\" (modelChange)=\"setScalar(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('val-' + index)\"></sd-date>\n }\n @case ('relative') {\n <sd-input-number class=\"qb-rel-amount\" type=\"positive\" [precision]=\"0\" size=\"sm\" [model]=\"relativeAmount(rule)\" (modelChange)=\"setRelativeAmount(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('rel-amount-' + index)\"></sd-input-number>\n <sd-select class=\"qb-rel-unit\" [items]=\"relativeUnitOptions\" valueField=\"value\" displayField=\"display\" size=\"sm\" [model]=\"relativeUnitDirValue(rule)\" (modelChange)=\"setRelativeUnitDir(rule, $event)\" [disabled]=\"resolvedDisabled()\" [clearable]=\"false\" hideInlineError [autoId]=\"autoIdFor('rel-unit-' + index)\"></sd-select>\n }\n }\n }\n @case ('datetime') {\n @let _dtMode = dateMode(rule);\n <sd-select class=\"qb-date-mode\" [items]=\"dateModes\" valueField=\"value\" displayField=\"display\" size=\"sm\" [model]=\"_dtMode\" (modelChange)=\"setDateMode(rule, $event)\" [disabled]=\"resolvedDisabled()\" [clearable]=\"false\" hideInlineError [autoId]=\"autoIdFor('datemode-' + index)\">\n <ng-template sdItemDef let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"$any(item).icon\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item).display }}</span>\n </span>\n </ng-template>\n <ng-template #sdSelected let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"$any(item)?.icon\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item)?.display }}</span>\n </span>\n </ng-template>\n </sd-select>\n @switch (_dtMode) {\n @case ('absolute') {\n <sd-datetime class=\"qb-val\" size=\"sm\" [model]=\"rule.value\" (modelChange)=\"setScalar(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('val-' + index)\"></sd-datetime>\n }\n @case ('relative') {\n <sd-input-number class=\"qb-rel-amount\" type=\"positive\" [precision]=\"0\" size=\"sm\" [model]=\"relativeAmount(rule)\" (modelChange)=\"setRelativeAmount(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('rel-amount-' + index)\"></sd-input-number>\n <sd-select class=\"qb-rel-unit\" [items]=\"relativeUnitOptions\" valueField=\"value\" displayField=\"display\" size=\"sm\" [model]=\"relativeUnitDirValue(rule)\" (modelChange)=\"setRelativeUnitDir(rule, $event)\" [disabled]=\"resolvedDisabled()\" [clearable]=\"false\" hideInlineError [autoId]=\"autoIdFor('rel-unit-' + index)\"></sd-select>\n }\n }\n }\n @case ('values') {\n <sd-select class=\"qb-val\" [items]=\"fieldOf(rule)!.values || []\" valueField=\"value\" displayField=\"display\" [multiple]=\"isMulti(rule.operator)\" placeholder=\"Gi\u00E1 tr\u1ECB\" size=\"sm\" [model]=\"rule.value\" (modelChange)=\"setScalar(rule, $event)\" [disabled]=\"resolvedDisabled()\" [clearable]=\"false\" hideInlineError [autoId]=\"autoIdFor('val-' + index)\"></sd-select>\n }\n }\n }\n }\n }\n\n <button type=\"button\" class=\"qb-btn qb-btn-remove-rule\" title=\"Xo\u00E1 \u0111i\u1EC1u ki\u1EC7n\" aria-label=\"Xo\u00E1 \u0111i\u1EC1u ki\u1EC7n\" [disabled]=\"resolvedDisabled()\" (click)=\"removeNode(parent, index)\">\u2715</button>\n </div>\n</ng-template>\n", styles: [":host{display:block;font-size:14px;color:var(--sd-text, #333)}.qb-view{display:block;width:100%;min-height:38px;padding:8px 12px;border:1px solid #d0d5dd;border-radius:6px;background:var(--sd-disabled-bg, #f6f7f9);color:var(--sd-text, #333);font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;font-size:13px;line-height:1.7;cursor:default;-webkit-user-select:text;user-select:text;white-space:pre-wrap;word-break:break-word}.qb-view-empty{color:#98a2b3}.qb-tok{white-space:pre}.qb-tok.qb-tok-field{color:var(--sd-text, #1d2939);font-weight:600}.qb-tok.qb-tok-op{color:var(--sd-primary, #005cbb);font-weight:600}.qb-tok.qb-tok-value{color:var(--sd-success, #1a7f37)}.qb-tok.qb-tok-logic{color:#98a2b3;font-weight:700;text-transform:uppercase}.qb-tok.qb-tok-paren{color:#667085}.qb-container{padding:16px;background:#fff;border:1px solid #e0e0e0;border-radius:6px}.qb-container.is-disabled{opacity:.7;pointer-events:none}.qb-header{display:flex;align-items:center;gap:8px;margin-bottom:12px;position:relative;z-index:1}.qb-header.is-open{z-index:1000}.qb-logic-switch{display:inline-flex;border:1px solid #e0e0e0;border-radius:4px;overflow:hidden;height:30px}.qb-logic-switch button{border:none;background:transparent;padding:0 14px;font-weight:600;font-size:12px;cursor:pointer;color:#667085;height:100%}.qb-logic-switch button.active{background:var(--sd-primary, #005cbb);color:#fff}.qb-logic-switch button:hover:not(.active):not([disabled]){background:var(--sd-primary-light, #d7e3ff);color:var(--sd-primary-dark, #1c4ad9)}.qb-logic-switch button[disabled]{cursor:default}.qb-actions{position:relative;display:flex;align-items:center;gap:4px}.qb-btn{background:transparent;border:none;cursor:pointer;font-size:16px;color:#667085;width:28px;height:28px;display:inline-flex;align-items:center;justify-content:center;border-radius:4px}.qb-btn:hover:not([disabled]){background:var(--sd-primary-light, #d7e3ff);color:var(--sd-primary, #005cbb)}.qb-btn[disabled]{cursor:default;color:#cbd2dc}.qb-btn-remove:hover:not([disabled]),.qb-btn-remove-rule:hover:not([disabled]){background:#fee4e2;color:#d92d20}.qb-btn-remove-rule{margin-left:auto}.qb-dropdown{position:absolute;top:100%;left:0;margin-top:4px;background:#fff;border:1px solid #e0e0e0;box-shadow:0 4px 12px #1018281f;border-radius:6px;min-width:150px;z-index:100;overflow:hidden}.qb-dropdown button{display:block;width:100%;text-align:left;background:none;border:none;padding:9px 14px;cursor:pointer;font-size:13px;color:#344054}.qb-dropdown button:hover{background:var(--sd-primary-light, #d7e3ff);color:var(--sd-primary, #005cbb)}.qb-body{padding-left:24px}.qb-treenode{position:relative;padding-left:22px;margin-bottom:12px}.qb-treenode:last-child{margin-bottom:0}.qb-treenode:before{content:\"\";position:absolute;left:0;top:-12px;bottom:0;border-left:1px dashed #d0d5dd}.qb-treenode:after{content:\"\";position:absolute;left:0;top:50%;width:18px;border-top:1px dashed #d0d5dd}.qb-treenode:last-child:before{bottom:auto;height:calc(50% + 12px)}.qb-empty{color:#98a2b3;font-style:italic;font-size:13px;padding:4px 0}.qb-rule{display:flex;align-items:center;gap:8px;background:#fafbfc;border:1px solid #e0e0e0;border-radius:6px;padding:6px 10px}.qb-rule:hover{border-color:#c2c8d0}.qb-field{min-width:170px}.qb-val{min-width:140px}.qb-value-source{min-width:132px}.qb-compare-field{min-width:170px}.qb-date-val{min-width:0;width:132px;flex:0 0 auto}::ng-deep .qb-opt{display:flex;align-items:center;gap:6px;min-width:0}::ng-deep .qb-opt .qb-opt-iconsd-icon{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;margin:0;font-size:18px;width:18px;height:18px;line-height:1;color:#667085}::ng-deep .qb-opt .qb-opt-label{min-width:0}.qb-between{display:flex;align-items:center;gap:6px}.qb-dash{color:#98a2b3}.qb-divider{width:1px;align-self:stretch;background:#e0e0e0}.qb-treenode.is-group>.qb-group{border:1px solid #e0e0e0;border-left:2px solid var(--sd-primary, #005cbb);border-radius:6px;padding:12px;background:#fff}.qb-date-mode{min-width:160px}.qb-rel-amount{width:72px}.qb-rel-unit{min-width:110px}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { 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"] }, { kind: "directive", type: SdItemDefDefDirective, selector: "[sdItemDef]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
940
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: SdQueryBuilder, isStandalone: true, selector: "sd-query-builder", inputs: { option: { classPropertyName: "option", publicName: "option", isSignal: true, isRequired: false, transformFunction: null }, fields: { classPropertyName: "fields", publicName: "fields", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, comparisonMode: { classPropertyName: "comparisonMode", publicName: "comparisonMode", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, autoId: { classPropertyName: "autoId", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, filters: { classPropertyName: "filters", publicName: "filters", isSignal: true, isRequired: false, transformFunction: null }, rootLogic: { classPropertyName: "rootLogic", publicName: "rootLogic", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", filters: "filtersChange", rootLogic: "rootLogicChange" }, ngImport: i0, template: "@if (isView()) {\n <div class=\"qb-view\" [attr.data-autoid]=\"autoIdFor('view')\" aria-readonly=\"true\">\n @if (tokens().length) {\n @for (t of tokens(); track $index) {\n <span class=\"qb-tok qb-tok-{{ t.kind }}\">{{ t.text }}</span>\n }\n } @else {\n <span class=\"qb-view-empty\">\u2014</span>\n }\n </div>\n} @else {\n <!-- why: \"click ra v\u00F9ng tr\u1ED1ng \u0111\u1EC3 \u0111\u00F3ng dropdown\" l\u00E0 h\u00E0nh vi CH\u1EC8 c\u00F3 \u1EDF chu\u1ED9t \u2014 ng\u01B0\u1EDDi d\u00F9ng b\u00E0n ph\u00EDm\n kh\u00F4ng c\u00F3 c\u00E1ch n\u00E0o \u0111\u00F3ng. Th\u00EAm Escape l\u00E0m t\u01B0\u01A1ng \u0111\u01B0\u01A1ng. role=\"group\": container l\u00E0 m\u1ED9t nh\u00F3m\n control n\u00EAn KH\u00D4NG nh\u1EADn tabindex, tab \u0111i th\u1EB3ng v\u00E0o c\u00E1c n\u00FAt/select b\u00EAn trong. -->\n <div\n class=\"qb-container\"\n role=\"group\"\n [class.is-disabled]=\"resolvedDisabled()\"\n (click)=\"closeAllDropdowns()\"\n (keydown.escape)=\"closeAllDropdowns()\">\n <ng-container [ngTemplateOutlet]=\"groupTpl\" [ngTemplateOutletContext]=\"{ $implicit: tree(), isRoot: true }\"></ng-container>\n </div>\n}\n\n<!-- Recursive group node -->\n<ng-template #groupTpl let-group let-isRoot=\"isRoot\" let-parent=\"parent\" let-index=\"index\">\n @let _removeGroupLabel = removeGroupLabel();\n <div class=\"qb-group\" [class.is-root]=\"isRoot\">\n <div class=\"qb-header\" [class.is-open]=\"group.open\">\n <div class=\"qb-logic-switch\">\n <button type=\"button\" [class.active]=\"group.logic === 'AND'\" [disabled]=\"resolvedDisabled()\" (click)=\"setGroupLogic(group, 'AND')\">AND</button>\n <button type=\"button\" [class.active]=\"group.logic === 'OR'\" [disabled]=\"resolvedDisabled()\" (click)=\"setGroupLogic(group, 'OR')\">OR</button>\n </div>\n\n <div class=\"qb-actions\">\n <button type=\"button\" class=\"qb-btn qb-btn-add\" [title]=\"addLabel()\" [attr.aria-label]=\"addNodeLabel()\" [disabled]=\"resolvedDisabled()\" (click)=\"toggleDropdown(group, $event)\">+</button>\n @if (group.open) {\n <!-- why: dropdown l\u00E0 NH\u00D3M hai n\u00FAt (role=\"group\"), kh\u00F4ng ph\u1EA3i n\u00FAt \u2014 kh\u00F4ng nh\u1EADn tabindex,\n tab \u0111i th\u1EB3ng v\u00E0o hai n\u00FAt. stopPropagation cho c\u1EA3 click l\u1EABn Enter \u0111\u1EC3 thao t\u00E1c b\u00EAn\n trong dropdown kh\u00F4ng k\u00EDch ho\u1EA1t handler \"\u0111\u00F3ng t\u1EA5t c\u1EA3\" c\u1EE7a container. -->\n <div class=\"qb-dropdown\" role=\"group\" (click)=\"$event.stopPropagation()\" (keydown.enter)=\"$event.stopPropagation()\">\n <button type=\"button\" (click)=\"addRule(group)\">+ {{ conditionLabel() }}</button>\n <button type=\"button\" (click)=\"addGroup(group)\">+ {{ groupLabel() }}</button>\n </div>\n }\n @if (!isRoot) {\n <button type=\"button\" class=\"qb-btn qb-btn-remove\" [title]=\"_removeGroupLabel\" [attr.aria-label]=\"_removeGroupLabel\" [disabled]=\"resolvedDisabled()\" (click)=\"removeNode(parent, index)\">\u2715</button>\n }\n </div>\n </div>\n\n <div class=\"qb-body\">\n @for (node of group.children; track node.id; let i = $index) {\n <div class=\"qb-treenode\" [class.is-group]=\"isGroup(node)\" [class.is-rule]=\"!isGroup(node)\">\n @if (isGroup(node)) {\n <ng-container\n [ngTemplateOutlet]=\"groupTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: node, isRoot: false, parent: group, index: i }\"\n ></ng-container>\n } @else {\n <ng-container [ngTemplateOutlet]=\"ruleTpl\" [ngTemplateOutletContext]=\"{ $implicit: node, parent: group, index: i }\"></ng-container>\n }\n </div>\n } @empty {\n <div class=\"qb-empty\">{{ emptyLabel() }}</div>\n }\n </div>\n </div>\n</ng-template>\n\n<!-- Single rule node: field \u2192 operator \u2192 value -->\n<ng-template #ruleTpl let-rule let-parent=\"parent\" let-index=\"index\">\n @let _selectFieldLabel = selectFieldLabel();\n @let _valueLabel = valueLabel();\n @let _removeConditionLabel = removeConditionLabel();\n @let _dateModes = dateModes();\n @let _relativeUnitOptions = relativeUnitOptions();\n <div class=\"qb-rule\">\n <sd-select\n class=\"qb-field\"\n [items]=\"resolvedFields()\"\n valueField=\"key\"\n displayField=\"label\"\n [placeholder]=\"_selectFieldLabel\"\n size=\"sm\"\n [model]=\"rule.field\"\n (modelChange)=\"setField(rule, $event)\"\n [disabled]=\"resolvedDisabled()\"\n [clearable]=\"false\"\n hideInlineError\n [autoId]=\"autoIdFor('field-' + index)\"\n >\n <!-- Option trong dropdown: icon th\u1EC3 hi\u1EC7n type + label. -->\n <ng-template sdItemDef let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"fieldIcon($any(item))\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item).label }}</span>\n </span>\n </ng-template>\n <!-- Gi\u00E1 tr\u1ECB \u0111\u00E3 ch\u1ECDn \u1EDF trigger: icon m\u1EB7c \u0111\u1ECBnh theo type khi field ch\u01B0a c\u00F3 icon ri\u00EAng. -->\n <ng-template #sdSelected let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"fieldIcon($any(item))\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item)?.label }}</span>\n </span>\n </ng-template>\n </sd-select>\n\n @if (rule.field) {\n <div class=\"qb-divider\"></div>\n\n @if (showOperatorSelector(rule)) {\n <sd-operator\n class=\"qb-op\"\n [operators]=\"allowedOperators(rule)\"\n [model]=\"rule.operator\"\n (modelChange)=\"setOperator(rule, $event)\"\n [disabled]=\"resolvedDisabled()\"\n [autoId]=\"autoIdFor('op-' + index)\"\n ></sd-operator>\n }\n\n @if (rule.operator && !isNoData(rule.operator)) {\n <div class=\"qb-divider\"></div>\n\n @if (showValueSourceSelector(rule)) {\n <sd-select\n class=\"qb-value-source\"\n [items]=\"valueSourceOptions()\"\n valueField=\"value\"\n displayField=\"display\"\n size=\"sm\"\n [model]=\"valueSourceOf(rule)\"\n (modelChange)=\"setValueSource(rule, $event)\"\n [disabled]=\"resolvedDisabled()\"\n [clearable]=\"false\"\n hideInlineError\n [autoId]=\"autoIdFor('valuesource-' + index)\"\n >\n <ng-template sdItemDef let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"$any(item).icon\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item).display }}</span>\n </span>\n </ng-template>\n <ng-template #sdSelected let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"$any(item)?.icon\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item)?.display }}</span>\n </span>\n </ng-template>\n </sd-select>\n <div class=\"qb-divider\"></div>\n }\n\n @if (isFieldSource(rule)) {\n <sd-select\n class=\"qb-val qb-compare-field\"\n [items]=\"compareFields(rule)\"\n valueField=\"key\"\n displayField=\"label\"\n [placeholder]=\"_selectFieldLabel\"\n size=\"sm\"\n [model]=\"rule.compareField\"\n (modelChange)=\"setCompareField(rule, $event)\"\n [disabled]=\"resolvedDisabled()\"\n [clearable]=\"false\"\n hideInlineError\n [autoId]=\"autoIdFor('compare-field-' + index)\"\n >\n <ng-template sdItemDef let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"fieldIcon($any(item))\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item).label }}</span>\n </span>\n </ng-template>\n <ng-template #sdSelected let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"fieldIcon($any(item))\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item)?.label }}</span>\n </span>\n </ng-template>\n </sd-select>\n } @else if (rule.operator === 'BETWEEN') {\n <div class=\"qb-between\">\n @switch (fieldOf(rule)?.type) {\n @case ('number') {\n <sd-input-number class=\"qb-val\" [placeholder]=\"fromLabel()\" size=\"sm\" [model]=\"rule.value?.from\" (modelChange)=\"setBetweenFrom(rule, $event)\" [min]=\"fieldOf(rule)?.min\" [max]=\"fieldOf(rule)?.max\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('from-' + index)\"></sd-input-number>\n <span class=\"qb-dash\">\u2014</span>\n <sd-input-number class=\"qb-val\" [placeholder]=\"toLabel()\" size=\"sm\" [model]=\"rule.value?.to\" (modelChange)=\"setBetweenTo(rule, $event)\" [min]=\"fieldOf(rule)?.min\" [max]=\"fieldOf(rule)?.max\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('to-' + index)\"></sd-input-number>\n }\n @case ('date') {\n <sd-date class=\"qb-val qb-date-val\" size=\"sm\" [model]=\"rule.value?.from\" (modelChange)=\"setBetweenFrom(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('from-' + index)\"></sd-date>\n <span class=\"qb-dash\">\u2014</span>\n <sd-date class=\"qb-val qb-date-val\" size=\"sm\" [model]=\"rule.value?.to\" (modelChange)=\"setBetweenTo(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('to-' + index)\"></sd-date>\n }\n @case ('datetime') {\n <sd-datetime class=\"qb-val\" size=\"sm\" [model]=\"rule.value?.from\" (modelChange)=\"setBetweenFrom(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('from-' + index)\"></sd-datetime>\n <span class=\"qb-dash\">\u2014</span>\n <sd-datetime class=\"qb-val\" size=\"sm\" [model]=\"rule.value?.to\" (modelChange)=\"setBetweenTo(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('to-' + index)\"></sd-datetime>\n }\n }\n </div>\n } @else {\n @switch (fieldOf(rule)?.type) {\n @case ('string') {\n <sd-input class=\"qb-val\" [placeholder]=\"_valueLabel\" size=\"sm\" [model]=\"rule.value\" (modelChange)=\"setScalar(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('val-' + index)\"></sd-input>\n }\n @case ('number') {\n <sd-input-number class=\"qb-val\" [placeholder]=\"_valueLabel\" size=\"sm\" [model]=\"rule.value\" (modelChange)=\"setScalar(rule, $event)\" [min]=\"fieldOf(rule)?.min\" [max]=\"fieldOf(rule)?.max\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('val-' + index)\"></sd-input-number>\n }\n @case ('boolean') {\n <sd-select class=\"qb-val\" [items]=\"booleanItems(fieldOf(rule)!)\" valueField=\"value\" displayField=\"display\" [placeholder]=\"_valueLabel\" size=\"sm\" [model]=\"rule.value\" (modelChange)=\"setScalar(rule, $event)\" [disabled]=\"resolvedDisabled()\" [clearable]=\"false\" hideInlineError [autoId]=\"autoIdFor('val-' + index)\"></sd-select>\n }\n @case ('date') {\n @let _dateMode = dateMode(rule);\n <sd-select class=\"qb-date-mode\" [items]=\"_dateModes\" valueField=\"value\" displayField=\"display\" size=\"sm\" [model]=\"_dateMode\" (modelChange)=\"setDateMode(rule, $event)\" [disabled]=\"resolvedDisabled()\" [clearable]=\"false\" hideInlineError [autoId]=\"autoIdFor('datemode-' + index)\">\n <ng-template sdItemDef let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"$any(item).icon\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item).display }}</span>\n </span>\n </ng-template>\n <ng-template #sdSelected let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"$any(item)?.icon\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item)?.display }}</span>\n </span>\n </ng-template>\n </sd-select>\n @switch (_dateMode) {\n @case ('absolute') {\n <sd-date class=\"qb-val qb-date-val\" size=\"sm\" [model]=\"rule.value\" (modelChange)=\"setScalar(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('val-' + index)\"></sd-date>\n }\n @case ('relative') {\n <sd-input-number class=\"qb-rel-amount\" type=\"positive\" [precision]=\"0\" size=\"sm\" [model]=\"relativeAmount(rule)\" (modelChange)=\"setRelativeAmount(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('rel-amount-' + index)\"></sd-input-number>\n <sd-select class=\"qb-rel-unit\" [items]=\"_relativeUnitOptions\" valueField=\"value\" displayField=\"display\" size=\"sm\" [model]=\"relativeUnitDirValue(rule)\" (modelChange)=\"setRelativeUnitDir(rule, $event)\" [disabled]=\"resolvedDisabled()\" [clearable]=\"false\" hideInlineError [autoId]=\"autoIdFor('rel-unit-' + index)\"></sd-select>\n }\n }\n }\n @case ('datetime') {\n @let _dtMode = dateMode(rule);\n <sd-select class=\"qb-date-mode\" [items]=\"_dateModes\" valueField=\"value\" displayField=\"display\" size=\"sm\" [model]=\"_dtMode\" (modelChange)=\"setDateMode(rule, $event)\" [disabled]=\"resolvedDisabled()\" [clearable]=\"false\" hideInlineError [autoId]=\"autoIdFor('datemode-' + index)\">\n <ng-template sdItemDef let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"$any(item).icon\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item).display }}</span>\n </span>\n </ng-template>\n <ng-template #sdSelected let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"$any(item)?.icon\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item)?.display }}</span>\n </span>\n </ng-template>\n </sd-select>\n @switch (_dtMode) {\n @case ('absolute') {\n <sd-datetime class=\"qb-val\" size=\"sm\" [model]=\"rule.value\" (modelChange)=\"setScalar(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('val-' + index)\"></sd-datetime>\n }\n @case ('relative') {\n <sd-input-number class=\"qb-rel-amount\" type=\"positive\" [precision]=\"0\" size=\"sm\" [model]=\"relativeAmount(rule)\" (modelChange)=\"setRelativeAmount(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('rel-amount-' + index)\"></sd-input-number>\n <sd-select class=\"qb-rel-unit\" [items]=\"_relativeUnitOptions\" valueField=\"value\" displayField=\"display\" size=\"sm\" [model]=\"relativeUnitDirValue(rule)\" (modelChange)=\"setRelativeUnitDir(rule, $event)\" [disabled]=\"resolvedDisabled()\" [clearable]=\"false\" hideInlineError [autoId]=\"autoIdFor('rel-unit-' + index)\"></sd-select>\n }\n }\n }\n @case ('values') {\n <sd-select class=\"qb-val\" [items]=\"valueItems(fieldOf(rule)!)\" valueField=\"value\" displayField=\"display\" [multiple]=\"isMulti(rule.operator)\" [placeholder]=\"_valueLabel\" size=\"sm\" [model]=\"rule.value\" (modelChange)=\"setScalar(rule, $event)\" [disabled]=\"resolvedDisabled()\" [clearable]=\"false\" hideInlineError [autoId]=\"autoIdFor('val-' + index)\"></sd-select>\n }\n }\n }\n }\n }\n\n <button type=\"button\" class=\"qb-btn qb-btn-remove-rule\" [title]=\"_removeConditionLabel\" [attr.aria-label]=\"_removeConditionLabel\" [disabled]=\"resolvedDisabled()\" (click)=\"removeNode(parent, index)\">\u2715</button>\n </div>\n</ng-template>\n", styles: [":host{display:block;font-size:14px;color:var(--sd-text, #333)}.qb-view{display:block;width:100%;min-height:38px;padding:8px 12px;border:1px solid #d0d5dd;border-radius:6px;background:var(--sd-disabled-bg, #f6f7f9);color:var(--sd-text, #333);font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;font-size:13px;line-height:1.7;cursor:default;-webkit-user-select:text;user-select:text;white-space:pre-wrap;word-break:break-word}.qb-view-empty{color:#98a2b3}.qb-tok{white-space:pre}.qb-tok.qb-tok-field{color:var(--sd-text, #1d2939);font-weight:600}.qb-tok.qb-tok-op{color:var(--sd-primary, #005cbb);font-weight:600}.qb-tok.qb-tok-value{color:var(--sd-success, #1a7f37)}.qb-tok.qb-tok-logic{color:#98a2b3;font-weight:700;text-transform:uppercase}.qb-tok.qb-tok-paren{color:#667085}.qb-container{padding:16px;background:#fff;border:1px solid #e0e0e0;border-radius:6px}.qb-container.is-disabled{opacity:.7;pointer-events:none}.qb-header{display:flex;align-items:center;gap:8px;margin-bottom:12px;position:relative;z-index:1}.qb-header.is-open{z-index:1000}.qb-logic-switch{display:inline-flex;border:1px solid #e0e0e0;border-radius:4px;overflow:hidden;height:30px}.qb-logic-switch button{border:none;background:transparent;padding:0 14px;font-weight:600;font-size:12px;cursor:pointer;color:#667085;height:100%}.qb-logic-switch button.active{background:var(--sd-primary, #005cbb);color:#fff}.qb-logic-switch button:hover:not(.active):not([disabled]){background:var(--sd-primary-light, #d7e3ff);color:var(--sd-primary-dark, #1c4ad9)}.qb-logic-switch button[disabled]{cursor:default}.qb-actions{position:relative;display:flex;align-items:center;gap:4px}.qb-btn{background:transparent;border:none;cursor:pointer;font-size:16px;color:#667085;width:28px;height:28px;display:inline-flex;align-items:center;justify-content:center;border-radius:4px}.qb-btn:hover:not([disabled]){background:var(--sd-primary-light, #d7e3ff);color:var(--sd-primary, #005cbb)}.qb-btn[disabled]{cursor:default;color:#cbd2dc}.qb-btn-remove:hover:not([disabled]),.qb-btn-remove-rule:hover:not([disabled]){background:#fee4e2;color:#d92d20}.qb-btn-remove-rule{margin-left:auto}.qb-dropdown{position:absolute;top:100%;left:0;margin-top:4px;background:#fff;border:1px solid #e0e0e0;box-shadow:0 4px 12px #1018281f;border-radius:6px;min-width:150px;z-index:100;overflow:hidden}.qb-dropdown button{display:block;width:100%;text-align:left;background:none;border:none;padding:9px 14px;cursor:pointer;font-size:13px;color:#344054}.qb-dropdown button:hover{background:var(--sd-primary-light, #d7e3ff);color:var(--sd-primary, #005cbb)}.qb-body{padding-left:24px}.qb-treenode{position:relative;padding-left:22px;margin-bottom:12px}.qb-treenode:last-child{margin-bottom:0}.qb-treenode:before{content:\"\";position:absolute;left:0;top:-12px;bottom:0;border-left:1px dashed #d0d5dd}.qb-treenode:after{content:\"\";position:absolute;left:0;top:50%;width:18px;border-top:1px dashed #d0d5dd}.qb-treenode:last-child:before{bottom:auto;height:calc(50% + 12px)}.qb-empty{color:#98a2b3;font-style:italic;font-size:13px;padding:4px 0}.qb-rule{display:flex;align-items:center;gap:8px;background:#fafbfc;border:1px solid #e0e0e0;border-radius:6px;padding:6px 10px}.qb-rule:hover{border-color:#c2c8d0}.qb-field{min-width:170px}.qb-val{min-width:140px}.qb-value-source{min-width:132px}.qb-compare-field{min-width:170px}.qb-date-val{min-width:0;width:132px;flex:0 0 auto}::ng-deep .qb-opt{display:flex;align-items:center;gap:6px;min-width:0}::ng-deep .qb-opt .qb-opt-iconsd-icon{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;margin:0;font-size:18px;width:18px;height:18px;line-height:1;color:#667085}::ng-deep .qb-opt .qb-opt-label{min-width:0}.qb-between{display:flex;align-items:center;gap:6px}.qb-dash{color:#98a2b3}.qb-divider{width:1px;align-self:stretch;background:#e0e0e0}.qb-treenode.is-group>.qb-group{border:1px solid #e0e0e0;border-left:2px solid var(--sd-primary, #005cbb);border-radius:6px;padding:12px;background:#fff}.qb-date-mode{min-width:160px}.qb-rel-amount{width:72px}.qb-rel-unit{min-width:110px}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { 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"] }, { kind: "directive", type: SdItemDefDefDirective, selector: "[sdItemDef]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
881
941
  }
882
942
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdQueryBuilder, decorators: [{
883
943
  type: Component,
884
- args: [{ selector: 'sd-query-builder', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [SdIcon, NgTemplateOutlet, SdOperator, SdSelect, SdInput, SdInputNumber, SdDate, SdDatetime, SdItemDefDefDirective], template: "@if (isView()) {\n <div class=\"qb-view\" [attr.data-autoid]=\"autoIdFor('view')\" aria-readonly=\"true\">\n @if (tokens().length) {\n @for (t of tokens(); track $index) {\n <span class=\"qb-tok qb-tok-{{ t.kind }}\">{{ t.text }}</span>\n }\n } @else {\n <span class=\"qb-view-empty\">\u2014</span>\n }\n </div>\n} @else {\n <div class=\"qb-container\" [class.is-disabled]=\"resolvedDisabled()\" (click)=\"closeAllDropdowns()\">\n <ng-container [ngTemplateOutlet]=\"groupTpl\" [ngTemplateOutletContext]=\"{ $implicit: tree(), isRoot: true }\"></ng-container>\n </div>\n}\n\n<!-- Recursive group node -->\n<ng-template #groupTpl let-group let-isRoot=\"isRoot\" let-parent=\"parent\" let-index=\"index\">\n <div class=\"qb-group\" [class.is-root]=\"isRoot\">\n <div class=\"qb-header\" [class.is-open]=\"group.open\">\n <div class=\"qb-logic-switch\">\n <button type=\"button\" [class.active]=\"group.logic === 'AND'\" [disabled]=\"resolvedDisabled()\" (click)=\"setGroupLogic(group, 'AND')\">AND</button>\n <button type=\"button\" [class.active]=\"group.logic === 'OR'\" [disabled]=\"resolvedDisabled()\" (click)=\"setGroupLogic(group, 'OR')\">OR</button>\n </div>\n\n <div class=\"qb-actions\">\n <button type=\"button\" class=\"qb-btn qb-btn-add\" title=\"Th\u00EAm\" aria-label=\"Th\u00EAm \u0111i\u1EC1u ki\u1EC7n ho\u1EB7c nh\u00F3m\" [disabled]=\"resolvedDisabled()\" (click)=\"toggleDropdown(group, $event)\">+</button>\n @if (group.open) {\n <div class=\"qb-dropdown\" (click)=\"$event.stopPropagation()\">\n <button type=\"button\" (click)=\"addRule(group)\">+ \u0110i\u1EC1u ki\u1EC7n</button>\n <button type=\"button\" (click)=\"addGroup(group)\">+ Nh\u00F3m</button>\n </div>\n }\n @if (!isRoot) {\n <button type=\"button\" class=\"qb-btn qb-btn-remove\" title=\"Xo\u00E1 nh\u00F3m\" aria-label=\"Xo\u00E1 nh\u00F3m\" [disabled]=\"resolvedDisabled()\" (click)=\"removeNode(parent, index)\">\u2715</button>\n }\n </div>\n </div>\n\n <div class=\"qb-body\">\n @for (node of group.children; track node.id; let i = $index) {\n <div class=\"qb-treenode\" [class.is-group]=\"isGroup(node)\" [class.is-rule]=\"!isGroup(node)\">\n @if (isGroup(node)) {\n <ng-container\n [ngTemplateOutlet]=\"groupTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: node, isRoot: false, parent: group, index: i }\"\n ></ng-container>\n } @else {\n <ng-container [ngTemplateOutlet]=\"ruleTpl\" [ngTemplateOutletContext]=\"{ $implicit: node, parent: group, index: i }\"></ng-container>\n }\n </div>\n } @empty {\n <div class=\"qb-empty\">Ch\u01B0a c\u00F3 \u0111i\u1EC1u ki\u1EC7n</div>\n }\n </div>\n </div>\n</ng-template>\n\n<!-- Single rule node: field \u2192 operator \u2192 value -->\n<ng-template #ruleTpl let-rule let-parent=\"parent\" let-index=\"index\">\n <div class=\"qb-rule\">\n <sd-select\n class=\"qb-field\"\n [items]=\"resolvedFields()\"\n valueField=\"key\"\n displayField=\"label\"\n placeholder=\"Ch\u1ECDn tr\u01B0\u1EDDng\"\n size=\"sm\"\n [model]=\"rule.field\"\n (modelChange)=\"setField(rule, $event)\"\n [disabled]=\"resolvedDisabled()\"\n [clearable]=\"false\"\n hideInlineError\n [autoId]=\"autoIdFor('field-' + index)\"\n >\n <!-- Option trong dropdown: icon th\u1EC3 hi\u1EC7n type + label. -->\n <ng-template sdItemDef let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"fieldIcon($any(item))\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item).label }}</span>\n </span>\n </ng-template>\n <!-- Gi\u00E1 tr\u1ECB \u0111\u00E3 ch\u1ECDn \u1EDF trigger: icon m\u1EB7c \u0111\u1ECBnh theo type khi field ch\u01B0a c\u00F3 icon ri\u00EAng. -->\n <ng-template #sdSelected let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"fieldIcon($any(item))\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item)?.label }}</span>\n </span>\n </ng-template>\n </sd-select>\n\n @if (rule.field) {\n <div class=\"qb-divider\"></div>\n\n @if (showOperatorSelector(rule)) {\n <sd-operator\n class=\"qb-op\"\n [operators]=\"allowedOperators(rule)\"\n [model]=\"rule.operator\"\n (modelChange)=\"setOperator(rule, $event)\"\n [disabled]=\"resolvedDisabled()\"\n [autoId]=\"autoIdFor('op-' + index)\"\n ></sd-operator>\n }\n\n @if (rule.operator && !isNoData(rule.operator)) {\n <div class=\"qb-divider\"></div>\n\n @if (showValueSourceSelector(rule)) {\n <sd-select\n class=\"qb-value-source\"\n [items]=\"valueSourceOptions\"\n valueField=\"value\"\n displayField=\"display\"\n size=\"sm\"\n [model]=\"valueSourceOf(rule)\"\n (modelChange)=\"setValueSource(rule, $event)\"\n [disabled]=\"resolvedDisabled()\"\n [clearable]=\"false\"\n hideInlineError\n [autoId]=\"autoIdFor('valuesource-' + index)\"\n >\n <ng-template sdItemDef let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"$any(item).icon\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item).display }}</span>\n </span>\n </ng-template>\n <ng-template #sdSelected let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"$any(item)?.icon\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item)?.display }}</span>\n </span>\n </ng-template>\n </sd-select>\n <div class=\"qb-divider\"></div>\n }\n\n @if (isFieldSource(rule)) {\n <sd-select\n class=\"qb-val qb-compare-field\"\n [items]=\"compareFields(rule)\"\n valueField=\"key\"\n displayField=\"label\"\n placeholder=\"Ch\u1ECDn tr\u01B0\u1EDDng\"\n size=\"sm\"\n [model]=\"rule.compareField\"\n (modelChange)=\"setCompareField(rule, $event)\"\n [disabled]=\"resolvedDisabled()\"\n [clearable]=\"false\"\n hideInlineError\n [autoId]=\"autoIdFor('compare-field-' + index)\"\n >\n <ng-template sdItemDef let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"fieldIcon($any(item))\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item).label }}</span>\n </span>\n </ng-template>\n <ng-template #sdSelected let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"fieldIcon($any(item))\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item)?.label }}</span>\n </span>\n </ng-template>\n </sd-select>\n } @else if (rule.operator === 'BETWEEN') {\n <div class=\"qb-between\">\n @switch (fieldOf(rule)?.type) {\n @case ('number') {\n <sd-input-number class=\"qb-val\" placeholder=\"T\u1EEB\" size=\"sm\" [model]=\"rule.value?.from\" (modelChange)=\"setBetweenFrom(rule, $event)\" [min]=\"fieldOf(rule)?.min\" [max]=\"fieldOf(rule)?.max\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('from-' + index)\"></sd-input-number>\n <span class=\"qb-dash\">\u2014</span>\n <sd-input-number class=\"qb-val\" placeholder=\"\u0110\u1EBFn\" size=\"sm\" [model]=\"rule.value?.to\" (modelChange)=\"setBetweenTo(rule, $event)\" [min]=\"fieldOf(rule)?.min\" [max]=\"fieldOf(rule)?.max\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('to-' + index)\"></sd-input-number>\n }\n @case ('date') {\n <sd-date class=\"qb-val qb-date-val\" size=\"sm\" [model]=\"rule.value?.from\" (modelChange)=\"setBetweenFrom(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('from-' + index)\"></sd-date>\n <span class=\"qb-dash\">\u2014</span>\n <sd-date class=\"qb-val qb-date-val\" size=\"sm\" [model]=\"rule.value?.to\" (modelChange)=\"setBetweenTo(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('to-' + index)\"></sd-date>\n }\n @case ('datetime') {\n <sd-datetime class=\"qb-val\" size=\"sm\" [model]=\"rule.value?.from\" (modelChange)=\"setBetweenFrom(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('from-' + index)\"></sd-datetime>\n <span class=\"qb-dash\">\u2014</span>\n <sd-datetime class=\"qb-val\" size=\"sm\" [model]=\"rule.value?.to\" (modelChange)=\"setBetweenTo(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('to-' + index)\"></sd-datetime>\n }\n }\n </div>\n } @else {\n @switch (fieldOf(rule)?.type) {\n @case ('string') {\n <sd-input class=\"qb-val\" placeholder=\"Gi\u00E1 tr\u1ECB\" size=\"sm\" [model]=\"rule.value\" (modelChange)=\"setScalar(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('val-' + index)\"></sd-input>\n }\n @case ('number') {\n <sd-input-number class=\"qb-val\" placeholder=\"Gi\u00E1 tr\u1ECB\" size=\"sm\" [model]=\"rule.value\" (modelChange)=\"setScalar(rule, $event)\" [min]=\"fieldOf(rule)?.min\" [max]=\"fieldOf(rule)?.max\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('val-' + index)\"></sd-input-number>\n }\n @case ('boolean') {\n <sd-select class=\"qb-val\" [items]=\"booleanItems(fieldOf(rule)!)\" valueField=\"value\" displayField=\"display\" placeholder=\"Gi\u00E1 tr\u1ECB\" size=\"sm\" [model]=\"rule.value\" (modelChange)=\"setScalar(rule, $event)\" [disabled]=\"resolvedDisabled()\" [clearable]=\"false\" hideInlineError [autoId]=\"autoIdFor('val-' + index)\"></sd-select>\n }\n @case ('date') {\n @let _dateMode = dateMode(rule);\n <sd-select class=\"qb-date-mode\" [items]=\"dateModes\" valueField=\"value\" displayField=\"display\" size=\"sm\" [model]=\"_dateMode\" (modelChange)=\"setDateMode(rule, $event)\" [disabled]=\"resolvedDisabled()\" [clearable]=\"false\" hideInlineError [autoId]=\"autoIdFor('datemode-' + index)\">\n <ng-template sdItemDef let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"$any(item).icon\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item).display }}</span>\n </span>\n </ng-template>\n <ng-template #sdSelected let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"$any(item)?.icon\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item)?.display }}</span>\n </span>\n </ng-template>\n </sd-select>\n @switch (_dateMode) {\n @case ('absolute') {\n <sd-date class=\"qb-val qb-date-val\" size=\"sm\" [model]=\"rule.value\" (modelChange)=\"setScalar(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('val-' + index)\"></sd-date>\n }\n @case ('relative') {\n <sd-input-number class=\"qb-rel-amount\" type=\"positive\" [precision]=\"0\" size=\"sm\" [model]=\"relativeAmount(rule)\" (modelChange)=\"setRelativeAmount(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('rel-amount-' + index)\"></sd-input-number>\n <sd-select class=\"qb-rel-unit\" [items]=\"relativeUnitOptions\" valueField=\"value\" displayField=\"display\" size=\"sm\" [model]=\"relativeUnitDirValue(rule)\" (modelChange)=\"setRelativeUnitDir(rule, $event)\" [disabled]=\"resolvedDisabled()\" [clearable]=\"false\" hideInlineError [autoId]=\"autoIdFor('rel-unit-' + index)\"></sd-select>\n }\n }\n }\n @case ('datetime') {\n @let _dtMode = dateMode(rule);\n <sd-select class=\"qb-date-mode\" [items]=\"dateModes\" valueField=\"value\" displayField=\"display\" size=\"sm\" [model]=\"_dtMode\" (modelChange)=\"setDateMode(rule, $event)\" [disabled]=\"resolvedDisabled()\" [clearable]=\"false\" hideInlineError [autoId]=\"autoIdFor('datemode-' + index)\">\n <ng-template sdItemDef let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"$any(item).icon\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item).display }}</span>\n </span>\n </ng-template>\n <ng-template #sdSelected let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"$any(item)?.icon\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item)?.display }}</span>\n </span>\n </ng-template>\n </sd-select>\n @switch (_dtMode) {\n @case ('absolute') {\n <sd-datetime class=\"qb-val\" size=\"sm\" [model]=\"rule.value\" (modelChange)=\"setScalar(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('val-' + index)\"></sd-datetime>\n }\n @case ('relative') {\n <sd-input-number class=\"qb-rel-amount\" type=\"positive\" [precision]=\"0\" size=\"sm\" [model]=\"relativeAmount(rule)\" (modelChange)=\"setRelativeAmount(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('rel-amount-' + index)\"></sd-input-number>\n <sd-select class=\"qb-rel-unit\" [items]=\"relativeUnitOptions\" valueField=\"value\" displayField=\"display\" size=\"sm\" [model]=\"relativeUnitDirValue(rule)\" (modelChange)=\"setRelativeUnitDir(rule, $event)\" [disabled]=\"resolvedDisabled()\" [clearable]=\"false\" hideInlineError [autoId]=\"autoIdFor('rel-unit-' + index)\"></sd-select>\n }\n }\n }\n @case ('values') {\n <sd-select class=\"qb-val\" [items]=\"fieldOf(rule)!.values || []\" valueField=\"value\" displayField=\"display\" [multiple]=\"isMulti(rule.operator)\" placeholder=\"Gi\u00E1 tr\u1ECB\" size=\"sm\" [model]=\"rule.value\" (modelChange)=\"setScalar(rule, $event)\" [disabled]=\"resolvedDisabled()\" [clearable]=\"false\" hideInlineError [autoId]=\"autoIdFor('val-' + index)\"></sd-select>\n }\n }\n }\n }\n }\n\n <button type=\"button\" class=\"qb-btn qb-btn-remove-rule\" title=\"Xo\u00E1 \u0111i\u1EC1u ki\u1EC7n\" aria-label=\"Xo\u00E1 \u0111i\u1EC1u ki\u1EC7n\" [disabled]=\"resolvedDisabled()\" (click)=\"removeNode(parent, index)\">\u2715</button>\n </div>\n</ng-template>\n", styles: [":host{display:block;font-size:14px;color:var(--sd-text, #333)}.qb-view{display:block;width:100%;min-height:38px;padding:8px 12px;border:1px solid #d0d5dd;border-radius:6px;background:var(--sd-disabled-bg, #f6f7f9);color:var(--sd-text, #333);font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;font-size:13px;line-height:1.7;cursor:default;-webkit-user-select:text;user-select:text;white-space:pre-wrap;word-break:break-word}.qb-view-empty{color:#98a2b3}.qb-tok{white-space:pre}.qb-tok.qb-tok-field{color:var(--sd-text, #1d2939);font-weight:600}.qb-tok.qb-tok-op{color:var(--sd-primary, #005cbb);font-weight:600}.qb-tok.qb-tok-value{color:var(--sd-success, #1a7f37)}.qb-tok.qb-tok-logic{color:#98a2b3;font-weight:700;text-transform:uppercase}.qb-tok.qb-tok-paren{color:#667085}.qb-container{padding:16px;background:#fff;border:1px solid #e0e0e0;border-radius:6px}.qb-container.is-disabled{opacity:.7;pointer-events:none}.qb-header{display:flex;align-items:center;gap:8px;margin-bottom:12px;position:relative;z-index:1}.qb-header.is-open{z-index:1000}.qb-logic-switch{display:inline-flex;border:1px solid #e0e0e0;border-radius:4px;overflow:hidden;height:30px}.qb-logic-switch button{border:none;background:transparent;padding:0 14px;font-weight:600;font-size:12px;cursor:pointer;color:#667085;height:100%}.qb-logic-switch button.active{background:var(--sd-primary, #005cbb);color:#fff}.qb-logic-switch button:hover:not(.active):not([disabled]){background:var(--sd-primary-light, #d7e3ff);color:var(--sd-primary-dark, #1c4ad9)}.qb-logic-switch button[disabled]{cursor:default}.qb-actions{position:relative;display:flex;align-items:center;gap:4px}.qb-btn{background:transparent;border:none;cursor:pointer;font-size:16px;color:#667085;width:28px;height:28px;display:inline-flex;align-items:center;justify-content:center;border-radius:4px}.qb-btn:hover:not([disabled]){background:var(--sd-primary-light, #d7e3ff);color:var(--sd-primary, #005cbb)}.qb-btn[disabled]{cursor:default;color:#cbd2dc}.qb-btn-remove:hover:not([disabled]),.qb-btn-remove-rule:hover:not([disabled]){background:#fee4e2;color:#d92d20}.qb-btn-remove-rule{margin-left:auto}.qb-dropdown{position:absolute;top:100%;left:0;margin-top:4px;background:#fff;border:1px solid #e0e0e0;box-shadow:0 4px 12px #1018281f;border-radius:6px;min-width:150px;z-index:100;overflow:hidden}.qb-dropdown button{display:block;width:100%;text-align:left;background:none;border:none;padding:9px 14px;cursor:pointer;font-size:13px;color:#344054}.qb-dropdown button:hover{background:var(--sd-primary-light, #d7e3ff);color:var(--sd-primary, #005cbb)}.qb-body{padding-left:24px}.qb-treenode{position:relative;padding-left:22px;margin-bottom:12px}.qb-treenode:last-child{margin-bottom:0}.qb-treenode:before{content:\"\";position:absolute;left:0;top:-12px;bottom:0;border-left:1px dashed #d0d5dd}.qb-treenode:after{content:\"\";position:absolute;left:0;top:50%;width:18px;border-top:1px dashed #d0d5dd}.qb-treenode:last-child:before{bottom:auto;height:calc(50% + 12px)}.qb-empty{color:#98a2b3;font-style:italic;font-size:13px;padding:4px 0}.qb-rule{display:flex;align-items:center;gap:8px;background:#fafbfc;border:1px solid #e0e0e0;border-radius:6px;padding:6px 10px}.qb-rule:hover{border-color:#c2c8d0}.qb-field{min-width:170px}.qb-val{min-width:140px}.qb-value-source{min-width:132px}.qb-compare-field{min-width:170px}.qb-date-val{min-width:0;width:132px;flex:0 0 auto}::ng-deep .qb-opt{display:flex;align-items:center;gap:6px;min-width:0}::ng-deep .qb-opt .qb-opt-iconsd-icon{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;margin:0;font-size:18px;width:18px;height:18px;line-height:1;color:#667085}::ng-deep .qb-opt .qb-opt-label{min-width:0}.qb-between{display:flex;align-items:center;gap:6px}.qb-dash{color:#98a2b3}.qb-divider{width:1px;align-self:stretch;background:#e0e0e0}.qb-treenode.is-group>.qb-group{border:1px solid #e0e0e0;border-left:2px solid var(--sd-primary, #005cbb);border-radius:6px;padding:12px;background:#fff}.qb-date-mode{min-width:160px}.qb-rel-amount{width:72px}.qb-rel-unit{min-width:110px}\n"] }]
944
+ args: [{ selector: 'sd-query-builder', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [SdIcon, NgTemplateOutlet, SdOperator, SdSelect, SdInput, SdInputNumber, SdDate, SdDatetime, SdItemDefDefDirective], template: "@if (isView()) {\n <div class=\"qb-view\" [attr.data-autoid]=\"autoIdFor('view')\" aria-readonly=\"true\">\n @if (tokens().length) {\n @for (t of tokens(); track $index) {\n <span class=\"qb-tok qb-tok-{{ t.kind }}\">{{ t.text }}</span>\n }\n } @else {\n <span class=\"qb-view-empty\">\u2014</span>\n }\n </div>\n} @else {\n <!-- why: \"click ra v\u00F9ng tr\u1ED1ng \u0111\u1EC3 \u0111\u00F3ng dropdown\" l\u00E0 h\u00E0nh vi CH\u1EC8 c\u00F3 \u1EDF chu\u1ED9t \u2014 ng\u01B0\u1EDDi d\u00F9ng b\u00E0n ph\u00EDm\n kh\u00F4ng c\u00F3 c\u00E1ch n\u00E0o \u0111\u00F3ng. Th\u00EAm Escape l\u00E0m t\u01B0\u01A1ng \u0111\u01B0\u01A1ng. role=\"group\": container l\u00E0 m\u1ED9t nh\u00F3m\n control n\u00EAn KH\u00D4NG nh\u1EADn tabindex, tab \u0111i th\u1EB3ng v\u00E0o c\u00E1c n\u00FAt/select b\u00EAn trong. -->\n <div\n class=\"qb-container\"\n role=\"group\"\n [class.is-disabled]=\"resolvedDisabled()\"\n (click)=\"closeAllDropdowns()\"\n (keydown.escape)=\"closeAllDropdowns()\">\n <ng-container [ngTemplateOutlet]=\"groupTpl\" [ngTemplateOutletContext]=\"{ $implicit: tree(), isRoot: true }\"></ng-container>\n </div>\n}\n\n<!-- Recursive group node -->\n<ng-template #groupTpl let-group let-isRoot=\"isRoot\" let-parent=\"parent\" let-index=\"index\">\n @let _removeGroupLabel = removeGroupLabel();\n <div class=\"qb-group\" [class.is-root]=\"isRoot\">\n <div class=\"qb-header\" [class.is-open]=\"group.open\">\n <div class=\"qb-logic-switch\">\n <button type=\"button\" [class.active]=\"group.logic === 'AND'\" [disabled]=\"resolvedDisabled()\" (click)=\"setGroupLogic(group, 'AND')\">AND</button>\n <button type=\"button\" [class.active]=\"group.logic === 'OR'\" [disabled]=\"resolvedDisabled()\" (click)=\"setGroupLogic(group, 'OR')\">OR</button>\n </div>\n\n <div class=\"qb-actions\">\n <button type=\"button\" class=\"qb-btn qb-btn-add\" [title]=\"addLabel()\" [attr.aria-label]=\"addNodeLabel()\" [disabled]=\"resolvedDisabled()\" (click)=\"toggleDropdown(group, $event)\">+</button>\n @if (group.open) {\n <!-- why: dropdown l\u00E0 NH\u00D3M hai n\u00FAt (role=\"group\"), kh\u00F4ng ph\u1EA3i n\u00FAt \u2014 kh\u00F4ng nh\u1EADn tabindex,\n tab \u0111i th\u1EB3ng v\u00E0o hai n\u00FAt. stopPropagation cho c\u1EA3 click l\u1EABn Enter \u0111\u1EC3 thao t\u00E1c b\u00EAn\n trong dropdown kh\u00F4ng k\u00EDch ho\u1EA1t handler \"\u0111\u00F3ng t\u1EA5t c\u1EA3\" c\u1EE7a container. -->\n <div class=\"qb-dropdown\" role=\"group\" (click)=\"$event.stopPropagation()\" (keydown.enter)=\"$event.stopPropagation()\">\n <button type=\"button\" (click)=\"addRule(group)\">+ {{ conditionLabel() }}</button>\n <button type=\"button\" (click)=\"addGroup(group)\">+ {{ groupLabel() }}</button>\n </div>\n }\n @if (!isRoot) {\n <button type=\"button\" class=\"qb-btn qb-btn-remove\" [title]=\"_removeGroupLabel\" [attr.aria-label]=\"_removeGroupLabel\" [disabled]=\"resolvedDisabled()\" (click)=\"removeNode(parent, index)\">\u2715</button>\n }\n </div>\n </div>\n\n <div class=\"qb-body\">\n @for (node of group.children; track node.id; let i = $index) {\n <div class=\"qb-treenode\" [class.is-group]=\"isGroup(node)\" [class.is-rule]=\"!isGroup(node)\">\n @if (isGroup(node)) {\n <ng-container\n [ngTemplateOutlet]=\"groupTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: node, isRoot: false, parent: group, index: i }\"\n ></ng-container>\n } @else {\n <ng-container [ngTemplateOutlet]=\"ruleTpl\" [ngTemplateOutletContext]=\"{ $implicit: node, parent: group, index: i }\"></ng-container>\n }\n </div>\n } @empty {\n <div class=\"qb-empty\">{{ emptyLabel() }}</div>\n }\n </div>\n </div>\n</ng-template>\n\n<!-- Single rule node: field \u2192 operator \u2192 value -->\n<ng-template #ruleTpl let-rule let-parent=\"parent\" let-index=\"index\">\n @let _selectFieldLabel = selectFieldLabel();\n @let _valueLabel = valueLabel();\n @let _removeConditionLabel = removeConditionLabel();\n @let _dateModes = dateModes();\n @let _relativeUnitOptions = relativeUnitOptions();\n <div class=\"qb-rule\">\n <sd-select\n class=\"qb-field\"\n [items]=\"resolvedFields()\"\n valueField=\"key\"\n displayField=\"label\"\n [placeholder]=\"_selectFieldLabel\"\n size=\"sm\"\n [model]=\"rule.field\"\n (modelChange)=\"setField(rule, $event)\"\n [disabled]=\"resolvedDisabled()\"\n [clearable]=\"false\"\n hideInlineError\n [autoId]=\"autoIdFor('field-' + index)\"\n >\n <!-- Option trong dropdown: icon th\u1EC3 hi\u1EC7n type + label. -->\n <ng-template sdItemDef let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"fieldIcon($any(item))\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item).label }}</span>\n </span>\n </ng-template>\n <!-- Gi\u00E1 tr\u1ECB \u0111\u00E3 ch\u1ECDn \u1EDF trigger: icon m\u1EB7c \u0111\u1ECBnh theo type khi field ch\u01B0a c\u00F3 icon ri\u00EAng. -->\n <ng-template #sdSelected let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"fieldIcon($any(item))\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item)?.label }}</span>\n </span>\n </ng-template>\n </sd-select>\n\n @if (rule.field) {\n <div class=\"qb-divider\"></div>\n\n @if (showOperatorSelector(rule)) {\n <sd-operator\n class=\"qb-op\"\n [operators]=\"allowedOperators(rule)\"\n [model]=\"rule.operator\"\n (modelChange)=\"setOperator(rule, $event)\"\n [disabled]=\"resolvedDisabled()\"\n [autoId]=\"autoIdFor('op-' + index)\"\n ></sd-operator>\n }\n\n @if (rule.operator && !isNoData(rule.operator)) {\n <div class=\"qb-divider\"></div>\n\n @if (showValueSourceSelector(rule)) {\n <sd-select\n class=\"qb-value-source\"\n [items]=\"valueSourceOptions()\"\n valueField=\"value\"\n displayField=\"display\"\n size=\"sm\"\n [model]=\"valueSourceOf(rule)\"\n (modelChange)=\"setValueSource(rule, $event)\"\n [disabled]=\"resolvedDisabled()\"\n [clearable]=\"false\"\n hideInlineError\n [autoId]=\"autoIdFor('valuesource-' + index)\"\n >\n <ng-template sdItemDef let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"$any(item).icon\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item).display }}</span>\n </span>\n </ng-template>\n <ng-template #sdSelected let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"$any(item)?.icon\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item)?.display }}</span>\n </span>\n </ng-template>\n </sd-select>\n <div class=\"qb-divider\"></div>\n }\n\n @if (isFieldSource(rule)) {\n <sd-select\n class=\"qb-val qb-compare-field\"\n [items]=\"compareFields(rule)\"\n valueField=\"key\"\n displayField=\"label\"\n [placeholder]=\"_selectFieldLabel\"\n size=\"sm\"\n [model]=\"rule.compareField\"\n (modelChange)=\"setCompareField(rule, $event)\"\n [disabled]=\"resolvedDisabled()\"\n [clearable]=\"false\"\n hideInlineError\n [autoId]=\"autoIdFor('compare-field-' + index)\"\n >\n <ng-template sdItemDef let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"fieldIcon($any(item))\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item).label }}</span>\n </span>\n </ng-template>\n <ng-template #sdSelected let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"fieldIcon($any(item))\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item)?.label }}</span>\n </span>\n </ng-template>\n </sd-select>\n } @else if (rule.operator === 'BETWEEN') {\n <div class=\"qb-between\">\n @switch (fieldOf(rule)?.type) {\n @case ('number') {\n <sd-input-number class=\"qb-val\" [placeholder]=\"fromLabel()\" size=\"sm\" [model]=\"rule.value?.from\" (modelChange)=\"setBetweenFrom(rule, $event)\" [min]=\"fieldOf(rule)?.min\" [max]=\"fieldOf(rule)?.max\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('from-' + index)\"></sd-input-number>\n <span class=\"qb-dash\">\u2014</span>\n <sd-input-number class=\"qb-val\" [placeholder]=\"toLabel()\" size=\"sm\" [model]=\"rule.value?.to\" (modelChange)=\"setBetweenTo(rule, $event)\" [min]=\"fieldOf(rule)?.min\" [max]=\"fieldOf(rule)?.max\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('to-' + index)\"></sd-input-number>\n }\n @case ('date') {\n <sd-date class=\"qb-val qb-date-val\" size=\"sm\" [model]=\"rule.value?.from\" (modelChange)=\"setBetweenFrom(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('from-' + index)\"></sd-date>\n <span class=\"qb-dash\">\u2014</span>\n <sd-date class=\"qb-val qb-date-val\" size=\"sm\" [model]=\"rule.value?.to\" (modelChange)=\"setBetweenTo(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('to-' + index)\"></sd-date>\n }\n @case ('datetime') {\n <sd-datetime class=\"qb-val\" size=\"sm\" [model]=\"rule.value?.from\" (modelChange)=\"setBetweenFrom(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('from-' + index)\"></sd-datetime>\n <span class=\"qb-dash\">\u2014</span>\n <sd-datetime class=\"qb-val\" size=\"sm\" [model]=\"rule.value?.to\" (modelChange)=\"setBetweenTo(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('to-' + index)\"></sd-datetime>\n }\n }\n </div>\n } @else {\n @switch (fieldOf(rule)?.type) {\n @case ('string') {\n <sd-input class=\"qb-val\" [placeholder]=\"_valueLabel\" size=\"sm\" [model]=\"rule.value\" (modelChange)=\"setScalar(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('val-' + index)\"></sd-input>\n }\n @case ('number') {\n <sd-input-number class=\"qb-val\" [placeholder]=\"_valueLabel\" size=\"sm\" [model]=\"rule.value\" (modelChange)=\"setScalar(rule, $event)\" [min]=\"fieldOf(rule)?.min\" [max]=\"fieldOf(rule)?.max\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('val-' + index)\"></sd-input-number>\n }\n @case ('boolean') {\n <sd-select class=\"qb-val\" [items]=\"booleanItems(fieldOf(rule)!)\" valueField=\"value\" displayField=\"display\" [placeholder]=\"_valueLabel\" size=\"sm\" [model]=\"rule.value\" (modelChange)=\"setScalar(rule, $event)\" [disabled]=\"resolvedDisabled()\" [clearable]=\"false\" hideInlineError [autoId]=\"autoIdFor('val-' + index)\"></sd-select>\n }\n @case ('date') {\n @let _dateMode = dateMode(rule);\n <sd-select class=\"qb-date-mode\" [items]=\"_dateModes\" valueField=\"value\" displayField=\"display\" size=\"sm\" [model]=\"_dateMode\" (modelChange)=\"setDateMode(rule, $event)\" [disabled]=\"resolvedDisabled()\" [clearable]=\"false\" hideInlineError [autoId]=\"autoIdFor('datemode-' + index)\">\n <ng-template sdItemDef let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"$any(item).icon\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item).display }}</span>\n </span>\n </ng-template>\n <ng-template #sdSelected let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"$any(item)?.icon\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item)?.display }}</span>\n </span>\n </ng-template>\n </sd-select>\n @switch (_dateMode) {\n @case ('absolute') {\n <sd-date class=\"qb-val qb-date-val\" size=\"sm\" [model]=\"rule.value\" (modelChange)=\"setScalar(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('val-' + index)\"></sd-date>\n }\n @case ('relative') {\n <sd-input-number class=\"qb-rel-amount\" type=\"positive\" [precision]=\"0\" size=\"sm\" [model]=\"relativeAmount(rule)\" (modelChange)=\"setRelativeAmount(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('rel-amount-' + index)\"></sd-input-number>\n <sd-select class=\"qb-rel-unit\" [items]=\"_relativeUnitOptions\" valueField=\"value\" displayField=\"display\" size=\"sm\" [model]=\"relativeUnitDirValue(rule)\" (modelChange)=\"setRelativeUnitDir(rule, $event)\" [disabled]=\"resolvedDisabled()\" [clearable]=\"false\" hideInlineError [autoId]=\"autoIdFor('rel-unit-' + index)\"></sd-select>\n }\n }\n }\n @case ('datetime') {\n @let _dtMode = dateMode(rule);\n <sd-select class=\"qb-date-mode\" [items]=\"_dateModes\" valueField=\"value\" displayField=\"display\" size=\"sm\" [model]=\"_dtMode\" (modelChange)=\"setDateMode(rule, $event)\" [disabled]=\"resolvedDisabled()\" [clearable]=\"false\" hideInlineError [autoId]=\"autoIdFor('datemode-' + index)\">\n <ng-template sdItemDef let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"$any(item).icon\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item).display }}</span>\n </span>\n </ng-template>\n <ng-template #sdSelected let-item=\"item\">\n <span class=\"qb-opt\">\n <sd-icon class=\"qb-opt-icon\" [name]=\"$any(item)?.icon\"></sd-icon>\n <span class=\"qb-opt-label\">{{ $any(item)?.display }}</span>\n </span>\n </ng-template>\n </sd-select>\n @switch (_dtMode) {\n @case ('absolute') {\n <sd-datetime class=\"qb-val\" size=\"sm\" [model]=\"rule.value\" (modelChange)=\"setScalar(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('val-' + index)\"></sd-datetime>\n }\n @case ('relative') {\n <sd-input-number class=\"qb-rel-amount\" type=\"positive\" [precision]=\"0\" size=\"sm\" [model]=\"relativeAmount(rule)\" (modelChange)=\"setRelativeAmount(rule, $event)\" [disabled]=\"resolvedDisabled()\" hideInlineError [autoId]=\"autoIdFor('rel-amount-' + index)\"></sd-input-number>\n <sd-select class=\"qb-rel-unit\" [items]=\"_relativeUnitOptions\" valueField=\"value\" displayField=\"display\" size=\"sm\" [model]=\"relativeUnitDirValue(rule)\" (modelChange)=\"setRelativeUnitDir(rule, $event)\" [disabled]=\"resolvedDisabled()\" [clearable]=\"false\" hideInlineError [autoId]=\"autoIdFor('rel-unit-' + index)\"></sd-select>\n }\n }\n }\n @case ('values') {\n <sd-select class=\"qb-val\" [items]=\"valueItems(fieldOf(rule)!)\" valueField=\"value\" displayField=\"display\" [multiple]=\"isMulti(rule.operator)\" [placeholder]=\"_valueLabel\" size=\"sm\" [model]=\"rule.value\" (modelChange)=\"setScalar(rule, $event)\" [disabled]=\"resolvedDisabled()\" [clearable]=\"false\" hideInlineError [autoId]=\"autoIdFor('val-' + index)\"></sd-select>\n }\n }\n }\n }\n }\n\n <button type=\"button\" class=\"qb-btn qb-btn-remove-rule\" [title]=\"_removeConditionLabel\" [attr.aria-label]=\"_removeConditionLabel\" [disabled]=\"resolvedDisabled()\" (click)=\"removeNode(parent, index)\">\u2715</button>\n </div>\n</ng-template>\n", styles: [":host{display:block;font-size:14px;color:var(--sd-text, #333)}.qb-view{display:block;width:100%;min-height:38px;padding:8px 12px;border:1px solid #d0d5dd;border-radius:6px;background:var(--sd-disabled-bg, #f6f7f9);color:var(--sd-text, #333);font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;font-size:13px;line-height:1.7;cursor:default;-webkit-user-select:text;user-select:text;white-space:pre-wrap;word-break:break-word}.qb-view-empty{color:#98a2b3}.qb-tok{white-space:pre}.qb-tok.qb-tok-field{color:var(--sd-text, #1d2939);font-weight:600}.qb-tok.qb-tok-op{color:var(--sd-primary, #005cbb);font-weight:600}.qb-tok.qb-tok-value{color:var(--sd-success, #1a7f37)}.qb-tok.qb-tok-logic{color:#98a2b3;font-weight:700;text-transform:uppercase}.qb-tok.qb-tok-paren{color:#667085}.qb-container{padding:16px;background:#fff;border:1px solid #e0e0e0;border-radius:6px}.qb-container.is-disabled{opacity:.7;pointer-events:none}.qb-header{display:flex;align-items:center;gap:8px;margin-bottom:12px;position:relative;z-index:1}.qb-header.is-open{z-index:1000}.qb-logic-switch{display:inline-flex;border:1px solid #e0e0e0;border-radius:4px;overflow:hidden;height:30px}.qb-logic-switch button{border:none;background:transparent;padding:0 14px;font-weight:600;font-size:12px;cursor:pointer;color:#667085;height:100%}.qb-logic-switch button.active{background:var(--sd-primary, #005cbb);color:#fff}.qb-logic-switch button:hover:not(.active):not([disabled]){background:var(--sd-primary-light, #d7e3ff);color:var(--sd-primary-dark, #1c4ad9)}.qb-logic-switch button[disabled]{cursor:default}.qb-actions{position:relative;display:flex;align-items:center;gap:4px}.qb-btn{background:transparent;border:none;cursor:pointer;font-size:16px;color:#667085;width:28px;height:28px;display:inline-flex;align-items:center;justify-content:center;border-radius:4px}.qb-btn:hover:not([disabled]){background:var(--sd-primary-light, #d7e3ff);color:var(--sd-primary, #005cbb)}.qb-btn[disabled]{cursor:default;color:#cbd2dc}.qb-btn-remove:hover:not([disabled]),.qb-btn-remove-rule:hover:not([disabled]){background:#fee4e2;color:#d92d20}.qb-btn-remove-rule{margin-left:auto}.qb-dropdown{position:absolute;top:100%;left:0;margin-top:4px;background:#fff;border:1px solid #e0e0e0;box-shadow:0 4px 12px #1018281f;border-radius:6px;min-width:150px;z-index:100;overflow:hidden}.qb-dropdown button{display:block;width:100%;text-align:left;background:none;border:none;padding:9px 14px;cursor:pointer;font-size:13px;color:#344054}.qb-dropdown button:hover{background:var(--sd-primary-light, #d7e3ff);color:var(--sd-primary, #005cbb)}.qb-body{padding-left:24px}.qb-treenode{position:relative;padding-left:22px;margin-bottom:12px}.qb-treenode:last-child{margin-bottom:0}.qb-treenode:before{content:\"\";position:absolute;left:0;top:-12px;bottom:0;border-left:1px dashed #d0d5dd}.qb-treenode:after{content:\"\";position:absolute;left:0;top:50%;width:18px;border-top:1px dashed #d0d5dd}.qb-treenode:last-child:before{bottom:auto;height:calc(50% + 12px)}.qb-empty{color:#98a2b3;font-style:italic;font-size:13px;padding:4px 0}.qb-rule{display:flex;align-items:center;gap:8px;background:#fafbfc;border:1px solid #e0e0e0;border-radius:6px;padding:6px 10px}.qb-rule:hover{border-color:#c2c8d0}.qb-field{min-width:170px}.qb-val{min-width:140px}.qb-value-source{min-width:132px}.qb-compare-field{min-width:170px}.qb-date-val{min-width:0;width:132px;flex:0 0 auto}::ng-deep .qb-opt{display:flex;align-items:center;gap:6px;min-width:0}::ng-deep .qb-opt .qb-opt-iconsd-icon{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;margin:0;font-size:18px;width:18px;height:18px;line-height:1;color:#667085}::ng-deep .qb-opt .qb-opt-label{min-width:0}.qb-between{display:flex;align-items:center;gap:6px}.qb-dash{color:#98a2b3}.qb-divider{width:1px;align-self:stretch;background:#e0e0e0}.qb-treenode.is-group>.qb-group{border:1px solid #e0e0e0;border-left:2px solid var(--sd-primary, #005cbb);border-radius:6px;padding:12px;background:#fff}.qb-date-mode{min-width:160px}.qb-rel-amount{width:72px}.qb-rel-unit{min-width:110px}\n"] }]
885
945
  }], ctorParameters: () => [] });
886
946
 
887
947
  /**
888
948
  * Generated bundle index. Do not edit.
889
949
  */
890
950
 
891
- export { QB_DATE_MODES, QB_DEFAULT_OPERATOR_BY_TYPE, QB_EMPTY_OPTIONS, QB_MULTI_OPERATORS, QB_NO_DATA_OPERATORS, QB_OPERATORS_BY_TYPE, QB_RELATIVE_UNIT_OPTIONS, QB_TODAY, QB_TYPE_ICON, QB_VALUE_SOURCE_OPTIONS, SdQueryBuilder, filterToTokens, filterToTree, isQbGroup, qbAllowedOperators, qbDefaultOperator, qbDefaultRelative, qbFieldIcon, qbId, qbIsMultiOperator, qbIsNoDataOperator, qbIsRelativeDate, qbIsToday, qbNewGroup, qbNewRule, qbSupportsFieldCompareOperator, treeToFilter };
951
+ export { SD_QB_DATE_MODES, SD_QB_DEFAULT_OPERATOR_BY_TYPE, SD_QB_EMPTY_OPTIONS, SD_QB_MULTI_OPERATORS, SD_QB_NO_DATA_OPERATORS, SD_QB_OPERATORS_BY_TYPE, SD_QB_RELATIVE_UNIT_OPTIONS, SD_QB_TODAY, SD_QB_TYPE_ICON, SD_QB_VALUE_SOURCE_OPTIONS, SdQueryBuilder, filterToTokens, filterToTree, qbRelativeLabelKey, sdIsQbGroup, sdQbAllowedOperators, sdQbDefaultOperator, sdQbDefaultRelative, sdQbFieldIcon, sdQbId, sdQbIsMultiOperator, sdQbIsNoDataOperator, sdQbIsRelativeDate, sdQbIsToday, sdQbNewGroup, sdQbNewRule, sdQbSupportsFieldCompareOperator, treeToFilter };
892
952
  //# sourceMappingURL=sdcorejs-angular-components-query-builder.mjs.map