@caribeng/ui 22.1.0 → 22.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 (253) hide show
  1. package/README.md +52 -12
  2. package/fesm2022/caribeng-ui-accordion.mjs +2 -2
  3. package/fesm2022/caribeng-ui-anchor.mjs +169 -0
  4. package/fesm2022/caribeng-ui-autocomplete.mjs +10 -8
  5. package/fesm2022/caribeng-ui-avatargroup.mjs +9 -3
  6. package/fesm2022/caribeng-ui-blockui.mjs +12 -4
  7. package/fesm2022/caribeng-ui-breadcrumb.mjs +5 -2
  8. package/fesm2022/caribeng-ui-button.mjs +22 -2
  9. package/fesm2022/caribeng-ui-caribe-alert.mjs +5 -2
  10. package/fesm2022/caribeng-ui-caribe-animation-lottie.mjs +6 -3
  11. package/fesm2022/caribeng-ui-caribe-button-group.mjs +6 -3
  12. package/fesm2022/caribeng-ui-caribe-calendar.mjs +11 -6
  13. package/fesm2022/caribeng-ui-caribe-card-content.mjs +6 -3
  14. package/fesm2022/caribeng-ui-caribe-card-move.mjs +29 -7
  15. package/fesm2022/caribeng-ui-caribe-card-select.mjs +56 -14
  16. package/fesm2022/caribeng-ui-caribe-circle-loading.mjs +10 -7
  17. package/fesm2022/caribeng-ui-caribe-coachmark.mjs +42 -19
  18. package/fesm2022/caribeng-ui-caribe-data-table.mjs +15 -9
  19. package/fesm2022/caribeng-ui-caribe-dots-indicator.mjs +38 -5
  20. package/fesm2022/caribeng-ui-caribe-expansion-panel.mjs +16 -5
  21. package/fesm2022/caribeng-ui-caribe-fab-menu.mjs +56 -7
  22. package/fesm2022/caribeng-ui-caribe-file-download.mjs +68 -27
  23. package/fesm2022/caribeng-ui-caribe-file-select.mjs +22 -17
  24. package/fesm2022/caribeng-ui-caribe-float-menu.mjs +6 -3
  25. package/fesm2022/caribeng-ui-caribe-footer.mjs +8 -4
  26. package/fesm2022/caribeng-ui-caribe-header.mjs +6 -3
  27. package/fesm2022/caribeng-ui-caribe-icon-button.mjs +6 -3
  28. package/fesm2022/caribeng-ui-caribe-illustration.mjs +6 -3
  29. package/fesm2022/caribeng-ui-caribe-inline-alert.mjs +5 -2
  30. package/fesm2022/caribeng-ui-caribe-input-search.mjs +14 -10
  31. package/fesm2022/caribeng-ui-caribe-input-select.mjs +12 -10
  32. package/fesm2022/caribeng-ui-caribe-input-token.mjs +61 -11
  33. package/fesm2022/caribeng-ui-caribe-link.mjs +7 -2
  34. package/fesm2022/caribeng-ui-caribe-loader.mjs +8 -5
  35. package/fesm2022/caribeng-ui-caribe-modal.mjs +32 -16
  36. package/fesm2022/caribeng-ui-caribe-off-canvas.mjs +26 -12
  37. package/fesm2022/caribeng-ui-caribe-onboarding.mjs +44 -19
  38. package/fesm2022/caribeng-ui-caribe-pictogram.mjs +6 -3
  39. package/fesm2022/caribeng-ui-caribe-pop-over.mjs +34 -10
  40. package/fesm2022/caribeng-ui-caribe-preloader.mjs +9 -6
  41. package/fesm2022/caribeng-ui-caribe-progress-bar.mjs +5 -2
  42. package/fesm2022/caribeng-ui-caribe-shortcut.mjs +8 -3
  43. package/fesm2022/caribeng-ui-caribe-step-indicator.mjs +12 -5
  44. package/fesm2022/caribeng-ui-caribe-stroke.mjs +6 -3
  45. package/fesm2022/caribeng-ui-caribe-switch.mjs +5 -3
  46. package/fesm2022/caribeng-ui-caribe-tag-button.mjs +1 -1
  47. package/fesm2022/caribeng-ui-caribe-tracker.mjs +5 -2
  48. package/fesm2022/caribeng-ui-caribe-transaction-status.mjs +1 -1
  49. package/fesm2022/caribeng-ui-caribe-voucher.mjs +11 -8
  50. package/fesm2022/caribeng-ui-carousel.mjs +49 -14
  51. package/fesm2022/caribeng-ui-cascadeselect.mjs +22 -8
  52. package/fesm2022/caribeng-ui-chart.mjs +2598 -303
  53. package/fesm2022/caribeng-ui-chip.mjs +9 -4
  54. package/fesm2022/caribeng-ui-chips.mjs +9 -5
  55. package/fesm2022/caribeng-ui-colorpicker.mjs +9 -7
  56. package/fesm2022/caribeng-ui-confirmdialog.mjs +74 -9
  57. package/fesm2022/caribeng-ui-confirmpopup.mjs +134 -9
  58. package/fesm2022/caribeng-ui-contextmenu.mjs +6 -3
  59. package/fesm2022/caribeng-ui-dataview.mjs +211 -6
  60. package/fesm2022/caribeng-ui-datepicker.mjs +1630 -393
  61. package/fesm2022/caribeng-ui-descriptions.mjs +124 -0
  62. package/fesm2022/caribeng-ui-dialog.mjs +10 -7
  63. package/fesm2022/caribeng-ui-dock.mjs +6 -3
  64. package/fesm2022/caribeng-ui-drawer.mjs +10 -7
  65. package/fesm2022/caribeng-ui-dynamicdialog.mjs +210 -6
  66. package/fesm2022/caribeng-ui-editor.mjs +4392 -243
  67. package/fesm2022/caribeng-ui-empty.mjs +80 -0
  68. package/fesm2022/caribeng-ui-file-card.mjs +2 -2
  69. package/fesm2022/caribeng-ui-fileupload.mjs +96 -19
  70. package/fesm2022/caribeng-ui-floatlabel.mjs +4 -2
  71. package/fesm2022/caribeng-ui-folder-card.mjs +10 -3
  72. package/fesm2022/caribeng-ui-galleria.mjs +76 -45
  73. package/fesm2022/caribeng-ui-image.mjs +52 -29
  74. package/fesm2022/caribeng-ui-inplace.mjs +22 -6
  75. package/fesm2022/caribeng-ui-input-number.mjs +9 -7
  76. package/fesm2022/caribeng-ui-inputgroup.mjs +2 -2
  77. package/fesm2022/caribeng-ui-inputmask.mjs +14 -11
  78. package/fesm2022/caribeng-ui-inputotp.mjs +12 -6
  79. package/fesm2022/caribeng-ui-keyfilter.mjs +72 -0
  80. package/fesm2022/caribeng-ui-listbox.mjs +13 -11
  81. package/fesm2022/caribeng-ui-megamenu.mjs +222 -30
  82. package/fesm2022/caribeng-ui-menu.mjs +11 -5
  83. package/fesm2022/caribeng-ui-menubar.mjs +10 -5
  84. package/fesm2022/caribeng-ui-message.mjs +7 -4
  85. package/fesm2022/caribeng-ui-metergroup.mjs +29 -11
  86. package/fesm2022/caribeng-ui-multiselect.mjs +399 -97
  87. package/fesm2022/caribeng-ui-orderlist.mjs +230 -11
  88. package/fesm2022/caribeng-ui-organizationchart.mjs +12 -7
  89. package/fesm2022/caribeng-ui-paginator.mjs +18 -15
  90. package/fesm2022/caribeng-ui-panel.mjs +5 -2
  91. package/fesm2022/caribeng-ui-panelmenu.mjs +6 -3
  92. package/fesm2022/caribeng-ui-password.mjs +8 -6
  93. package/fesm2022/caribeng-ui-pdfviewer.mjs +1553 -0
  94. package/fesm2022/caribeng-ui-picklist.mjs +251 -14
  95. package/fesm2022/caribeng-ui-pricing.mjs +17 -14
  96. package/fesm2022/caribeng-ui-rating.mjs +7 -3
  97. package/fesm2022/caribeng-ui-repeater.mjs +223 -0
  98. package/fesm2022/caribeng-ui-result.mjs +91 -0
  99. package/fesm2022/caribeng-ui-scrollpanel.mjs +6 -3
  100. package/fesm2022/caribeng-ui-scrolltop.mjs +6 -3
  101. package/fesm2022/caribeng-ui-select.mjs +379 -87
  102. package/fesm2022/caribeng-ui-selectbutton.mjs +2 -2
  103. package/fesm2022/caribeng-ui-slider.mjs +171 -75
  104. package/fesm2022/caribeng-ui-speeddial.mjs +7 -4
  105. package/fesm2022/caribeng-ui-spinner.mjs +5 -2
  106. package/fesm2022/caribeng-ui-splitbutton.mjs +163 -0
  107. package/fesm2022/caribeng-ui-splitter.mjs +5 -2
  108. package/fesm2022/caribeng-ui-statistic.mjs +183 -0
  109. package/fesm2022/caribeng-ui-stepper.mjs +8 -5
  110. package/fesm2022/caribeng-ui-steps.mjs +6 -3
  111. package/fesm2022/caribeng-ui-table.mjs +708 -82
  112. package/fesm2022/caribeng-ui-tabmenu.mjs +6 -3
  113. package/fesm2022/caribeng-ui-tag.mjs +2 -2
  114. package/fesm2022/caribeng-ui-terminal.mjs +45 -9
  115. package/fesm2022/caribeng-ui-themetoggle.mjs +82 -0
  116. package/fesm2022/caribeng-ui-tieredmenu.mjs +9 -6
  117. package/fesm2022/caribeng-ui-timeline.mjs +175 -6
  118. package/fesm2022/caribeng-ui-toast.mjs +7 -4
  119. package/fesm2022/caribeng-ui-togglebutton.mjs +8 -6
  120. package/fesm2022/caribeng-ui-toolbar.mjs +6 -3
  121. package/fesm2022/caribeng-ui-tree.mjs +302 -6
  122. package/fesm2022/caribeng-ui-treeselect.mjs +26 -10
  123. package/fesm2022/caribeng-ui-treetable.mjs +269 -6
  124. package/fesm2022/caribeng-ui-virtualscroller.mjs +123 -10
  125. package/fesm2022/caribeng-ui.mjs +829 -6
  126. package/package.json +70 -11
  127. package/schematics/collection.json +10 -0
  128. package/schematics/ng-add/index.js +125 -0
  129. package/schematics/ng-add/schema.js +2 -0
  130. package/schematics/ng-add/schema.json +66 -0
  131. package/styles/caribeng.css +380 -0
  132. package/styles/scss/_mixins.scss +46 -0
  133. package/styles/scss/_overlay.scss +15 -0
  134. package/styles/scss/_tokens.scss +267 -0
  135. package/styles/scss/_utilities.scss +73 -0
  136. package/styles/scss/_variables.scss +33 -0
  137. package/styles/scss/index.scss +16 -0
  138. package/types/caribeng-ui-anchor.d.ts +50 -0
  139. package/types/caribeng-ui-autocomplete.d.ts +4 -2
  140. package/types/caribeng-ui-avatargroup.d.ts +3 -1
  141. package/types/caribeng-ui-blockui.d.ts +9 -4
  142. package/types/caribeng-ui-breadcrumb.d.ts +3 -0
  143. package/types/caribeng-ui-button.d.ts +9 -1
  144. package/types/caribeng-ui-caribe-alert.d.ts +3 -0
  145. package/types/caribeng-ui-caribe-animation-lottie.d.ts +4 -1
  146. package/types/caribeng-ui-caribe-button-group.d.ts +4 -1
  147. package/types/caribeng-ui-caribe-calendar.d.ts +5 -1
  148. package/types/caribeng-ui-caribe-card-content.d.ts +4 -1
  149. package/types/caribeng-ui-caribe-card-move.d.ts +13 -13
  150. package/types/caribeng-ui-caribe-card-select.d.ts +10 -1
  151. package/types/caribeng-ui-caribe-circle-loading.d.ts +4 -1
  152. package/types/caribeng-ui-caribe-coachmark.d.ts +6 -0
  153. package/types/caribeng-ui-caribe-data-table.d.ts +7 -2
  154. package/types/caribeng-ui-caribe-dots-indicator.d.ts +9 -0
  155. package/types/caribeng-ui-caribe-expansion-panel.d.ts +7 -10
  156. package/types/caribeng-ui-caribe-fab-menu.d.ts +10 -1
  157. package/types/caribeng-ui-caribe-file-download.d.ts +8 -10
  158. package/types/caribeng-ui-caribe-file-select.d.ts +7 -1
  159. package/types/caribeng-ui-caribe-float-menu.d.ts +4 -1
  160. package/types/caribeng-ui-caribe-header.d.ts +3 -0
  161. package/types/caribeng-ui-caribe-icon-button.d.ts +4 -1
  162. package/types/caribeng-ui-caribe-illustration.d.ts +4 -1
  163. package/types/caribeng-ui-caribe-inline-alert.d.ts +3 -0
  164. package/types/caribeng-ui-caribe-input-search.d.ts +4 -2
  165. package/types/caribeng-ui-caribe-input-select.d.ts +4 -2
  166. package/types/caribeng-ui-caribe-input-token.d.ts +16 -1
  167. package/types/caribeng-ui-caribe-link.d.ts +3 -0
  168. package/types/caribeng-ui-caribe-loader.d.ts +4 -1
  169. package/types/caribeng-ui-caribe-modal.d.ts +7 -10
  170. package/types/caribeng-ui-caribe-off-canvas.d.ts +6 -1
  171. package/types/caribeng-ui-caribe-onboarding.d.ts +6 -0
  172. package/types/caribeng-ui-caribe-pictogram.d.ts +4 -1
  173. package/types/caribeng-ui-caribe-pop-over.d.ts +7 -10
  174. package/types/caribeng-ui-caribe-preloader.d.ts +4 -1
  175. package/types/caribeng-ui-caribe-progress-bar.d.ts +3 -0
  176. package/types/caribeng-ui-caribe-shortcut.d.ts +3 -1
  177. package/types/caribeng-ui-caribe-step-indicator.d.ts +6 -0
  178. package/types/caribeng-ui-caribe-stroke.d.ts +4 -1
  179. package/types/caribeng-ui-caribe-switch.d.ts +3 -1
  180. package/types/caribeng-ui-caribe-tracker.d.ts +3 -0
  181. package/types/caribeng-ui-caribe-voucher.d.ts +5 -2
  182. package/types/caribeng-ui-carousel.d.ts +10 -1
  183. package/types/caribeng-ui-cascadeselect.d.ts +7 -2
  184. package/types/caribeng-ui-chart.d.ts +469 -44
  185. package/types/caribeng-ui-chip.d.ts +6 -0
  186. package/types/caribeng-ui-chips.d.ts +6 -2
  187. package/types/caribeng-ui-colorpicker.d.ts +3 -1
  188. package/types/caribeng-ui-confirmdialog.d.ts +25 -6
  189. package/types/caribeng-ui-confirmpopup.d.ts +37 -5
  190. package/types/caribeng-ui-contextmenu.d.ts +4 -1
  191. package/types/caribeng-ui-dataview.d.ts +63 -5
  192. package/types/caribeng-ui-datepicker.d.ts +186 -21
  193. package/types/caribeng-ui-descriptions.d.ts +63 -0
  194. package/types/caribeng-ui-dialog.d.ts +4 -1
  195. package/types/caribeng-ui-dock.d.ts +4 -1
  196. package/types/caribeng-ui-drawer.d.ts +4 -1
  197. package/types/caribeng-ui-dynamicdialog.d.ts +93 -3
  198. package/types/caribeng-ui-editor.d.ts +486 -13
  199. package/types/caribeng-ui-empty.d.ts +34 -0
  200. package/types/caribeng-ui-file-card.d.ts +1 -1
  201. package/types/caribeng-ui-fileupload.d.ts +27 -4
  202. package/types/caribeng-ui-folder-card.d.ts +4 -1
  203. package/types/caribeng-ui-galleria.d.ts +9 -0
  204. package/types/caribeng-ui-image.d.ts +10 -0
  205. package/types/caribeng-ui-inplace.d.ts +8 -0
  206. package/types/caribeng-ui-input-number.d.ts +3 -1
  207. package/types/caribeng-ui-inputmask.d.ts +5 -0
  208. package/types/caribeng-ui-inputotp.d.ts +7 -2
  209. package/types/caribeng-ui-keyfilter.d.ts +30 -0
  210. package/types/caribeng-ui-listbox.d.ts +5 -3
  211. package/types/caribeng-ui-megamenu.d.ts +26 -2
  212. package/types/caribeng-ui-menu.d.ts +7 -2
  213. package/types/caribeng-ui-menubar.d.ts +5 -1
  214. package/types/caribeng-ui-message.d.ts +3 -0
  215. package/types/caribeng-ui-metergroup.d.ts +9 -1
  216. package/types/caribeng-ui-multiselect.d.ts +71 -10
  217. package/types/caribeng-ui-orderlist.d.ts +58 -6
  218. package/types/caribeng-ui-organizationchart.d.ts +7 -1
  219. package/types/caribeng-ui-paginator.d.ts +3 -0
  220. package/types/caribeng-ui-panel.d.ts +3 -0
  221. package/types/caribeng-ui-panelmenu.d.ts +4 -1
  222. package/types/caribeng-ui-password.d.ts +4 -2
  223. package/types/caribeng-ui-pdfviewer.d.ts +258 -0
  224. package/types/caribeng-ui-picklist.d.ts +67 -7
  225. package/types/caribeng-ui-pricing.d.ts +8 -5
  226. package/types/caribeng-ui-rating.d.ts +6 -2
  227. package/types/caribeng-ui-repeater.d.ts +79 -0
  228. package/types/caribeng-ui-result.d.ts +35 -0
  229. package/types/caribeng-ui-scrollpanel.d.ts +4 -1
  230. package/types/caribeng-ui-scrolltop.d.ts +4 -1
  231. package/types/caribeng-ui-select.d.ts +73 -9
  232. package/types/caribeng-ui-slider.d.ts +63 -10
  233. package/types/caribeng-ui-speeddial.d.ts +5 -2
  234. package/types/caribeng-ui-spinner.d.ts +3 -0
  235. package/types/caribeng-ui-splitbutton.d.ts +49 -0
  236. package/types/caribeng-ui-splitter.d.ts +3 -0
  237. package/types/caribeng-ui-statistic.d.ts +65 -0
  238. package/types/caribeng-ui-stepper.d.ts +3 -0
  239. package/types/caribeng-ui-steps.d.ts +4 -1
  240. package/types/caribeng-ui-table.d.ts +184 -16
  241. package/types/caribeng-ui-tabmenu.d.ts +4 -1
  242. package/types/caribeng-ui-terminal.d.ts +10 -0
  243. package/types/caribeng-ui-themetoggle.d.ts +32 -0
  244. package/types/caribeng-ui-tieredmenu.d.ts +5 -2
  245. package/types/caribeng-ui-timeline.d.ts +63 -4
  246. package/types/caribeng-ui-toast.d.ts +3 -0
  247. package/types/caribeng-ui-togglebutton.d.ts +5 -3
  248. package/types/caribeng-ui-toolbar.d.ts +7 -4
  249. package/types/caribeng-ui-tree.d.ts +76 -4
  250. package/types/caribeng-ui-treeselect.d.ts +10 -2
  251. package/types/caribeng-ui-treetable.d.ts +68 -4
  252. package/types/caribeng-ui-virtualscroller.d.ts +45 -5
  253. package/types/caribeng-ui.d.ts +501 -12
@@ -1,15 +1,20 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, inject, TemplateRef, Directive, contentChildren, model, output, signal, computed, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
3
- import { NgTemplateOutlet } from '@angular/common';
2
+ import { input, inject, TemplateRef, Directive, contentChildren, contentChild, viewChild, model, output, signal, computed, linkedSignal, DestroyRef, afterNextRender, afterEveryRender, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { DOCUMENT, NgTemplateOutlet } from '@angular/common';
4
4
  import { CbIcon } from '@caribeng/ui/icon';
5
5
  import { CbPaginator } from '@caribeng/ui/paginator';
6
+ import { injectCbTranslation, cbFormat } from '@caribeng/ui';
6
7
 
8
+ /** Plantilla de las celdas de una columna: `<ng-template cbTableCell="campo" let-row let-value="value">`. */
7
9
  class CbTableCellDirective {
8
10
  constructor() {
9
11
  this.cbTableCell = input.required(/* @ts-ignore */
10
12
  ...(ngDevMode ? [{ debugName: "cbTableCell" }] : /* istanbul ignore next */ []));
11
13
  this.template = inject(TemplateRef);
12
14
  }
15
+ static ngTemplateContextGuard(_dir, ctx) {
16
+ return true;
17
+ }
13
18
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbTableCellDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
14
19
  static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.5", type: CbTableCellDirective, isStandalone: true, selector: "ng-template[cbTableCell]", inputs: { cbTableCell: { classPropertyName: "cbTableCell", publicName: "cbTableCell", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 }); }
15
20
  }
@@ -20,20 +25,77 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
20
25
  standalone: true,
21
26
  }]
22
27
  }], propDecorators: { cbTableCell: [{ type: i0.Input, args: [{ isSignal: true, alias: "cbTableCell", required: true }] }] } });
28
+ /** Contenido de la fila expandida: `<ng-template cbTableRowExpansion let-row>`. */
29
+ class CbTableRowExpansion {
30
+ constructor() {
31
+ this.template = inject(TemplateRef);
32
+ }
33
+ static ngTemplateContextGuard(_dir, ctx) {
34
+ return true;
35
+ }
36
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbTableRowExpansion, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
37
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.5", type: CbTableRowExpansion, isStandalone: true, selector: "ng-template[cbTableRowExpansion]", ngImport: i0 }); }
38
+ }
39
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbTableRowExpansion, decorators: [{
40
+ type: Directive,
41
+ args: [{
42
+ selector: 'ng-template[cbTableRowExpansion]',
43
+ standalone: true,
44
+ }]
45
+ }] });
46
+ /** Cabecera de grupo: `<ng-template cbTableGroupHeader let-valor let-count="count">`. */
47
+ class CbTableGroupHeader {
48
+ constructor() {
49
+ this.template = inject(TemplateRef);
50
+ }
51
+ static ngTemplateContextGuard(_dir, ctx) {
52
+ return true;
53
+ }
54
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbTableGroupHeader, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
55
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.5", type: CbTableGroupHeader, isStandalone: true, selector: "ng-template[cbTableGroupHeader]", ngImport: i0 }); }
56
+ }
57
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbTableGroupHeader, decorators: [{
58
+ type: Directive,
59
+ args: [{
60
+ selector: 'ng-template[cbTableGroupHeader]',
61
+ standalone: true,
62
+ }]
63
+ }] });
64
+ let nextId = 0;
65
+ const MIN_COLUMN_WIDTH = 48;
23
66
  /**
24
67
  * Table de CaribeNG: tabla de datos con columnas declarativas, ordenación,
25
- * paginación y selección (simple/múltiple).
68
+ * filtros (global y por columna), paginación, selección (simple/múltiple),
69
+ * columnas redimensionables y fijas, cabecera fija, filas expandibles y
70
+ * agrupadas, exportación a CSV y carga desde el servidor (`lazy`).
26
71
  *
27
72
  * @example
28
73
  * ```html
29
- * <cb-table [value]="productos" [columns]="cols" [paginator]="true" [rows]="5"
30
- * striped selectionMode="multiple" dataKey="id" [(selection)]="sel" />
74
+ * <input type="search" [(ngModel)]="busqueda" aria-label="Buscar" />
75
+ * <cb-table [value]="productos" [columns]="cols" [globalFilter]="busqueda" [paginator]="true" [rows]="5"
76
+ * striped selectionMode="multiple" dataKey="id" [(selection)]="sel" resizableColumns>
77
+ * <ng-template cbTableRowExpansion let-producto>{{ producto.descripcion }}</ng-template>
78
+ * </cb-table>
31
79
  * ```
32
80
  */
33
81
  class CbTable {
34
82
  constructor() {
83
+ /** Textos de interfaz en el idioma activo (`provideCaribeNG({ locale })`). */
84
+ this.t = injectCbTranslation();
85
+ /** Rellena `{0}`, `{1}`… de un texto traducido. */
86
+ this.fmt = cbFormat;
87
+ this.minColumnWidth = MIN_COLUMN_WIDTH;
88
+ this.document = inject(DOCUMENT);
35
89
  this.cellTemplates = contentChildren(CbTableCellDirective, /* @ts-ignore */
36
90
  ...(ngDevMode ? [{ debugName: "cellTemplates" }] : /* istanbul ignore next */ []));
91
+ /** Plantilla de fila expandida; si existe, se añade la columna de expandir. */
92
+ this.expansion = contentChild(CbTableRowExpansion, /* @ts-ignore */
93
+ ...(ngDevMode ? [{ debugName: "expansion" }] : /* istanbul ignore next */ []));
94
+ /** Plantilla de cabecera de grupo (con `groupRowsBy`). */
95
+ this.groupHeader = contentChild(CbTableGroupHeader, /* @ts-ignore */
96
+ ...(ngDevMode ? [{ debugName: "groupHeader" }] : /* istanbul ignore next */ []));
97
+ this.tableRef = viewChild('table', /* @ts-ignore */
98
+ ...(ngDevMode ? [{ debugName: "tableRef" }] : /* istanbul ignore next */ []));
37
99
  this.value = input([], /* @ts-ignore */
38
100
  ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
39
101
  this.columns = input([], /* @ts-ignore */
@@ -47,50 +109,194 @@ class CbTable {
47
109
  this.rowHover = input(true, { ...(ngDevMode ? { debugName: "rowHover" } : /* istanbul ignore next */ {}), transform: toBoolean });
48
110
  this.size = input('md', /* @ts-ignore */
49
111
  ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
50
- this.emptyMessage = input('Sin datos', /* @ts-ignore */
112
+ this.emptyMessage = input(undefined, /* @ts-ignore */
51
113
  ...(ngDevMode ? [{ debugName: "emptyMessage" }] : /* istanbul ignore next */ []));
114
+ /** Nombre accesible de la tabla. */
115
+ this.ariaLabel = input(undefined, /* @ts-ignore */
116
+ ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
52
117
  /** `single`, `multiple` o `null` (sin selección). */
53
118
  this.selectionMode = input(null, /* @ts-ignore */
54
119
  ...(ngDevMode ? [{ debugName: "selectionMode" }] : /* istanbul ignore next */ []));
55
- /** Propiedad identificadora para comparar selección (si no, por referencia). */
120
+ /** Propiedad identificadora para comparar selección y filas expandidas (si no, por referencia). */
56
121
  this.dataKey = input(null, /* @ts-ignore */
57
122
  ...(ngDevMode ? [{ debugName: "dataKey" }] : /* istanbul ignore next */ []));
123
+ /** Texto del filtro global (two-way). Busca en `globalFilterFields`. */
124
+ this.globalFilter = model('', /* @ts-ignore */
125
+ ...(ngDevMode ? [{ debugName: "globalFilter" }] : /* istanbul ignore next */ []));
126
+ /** Campos en los que busca el filtro global. Por defecto, los de todas las columnas. */
127
+ this.globalFilterFields = input(null, /* @ts-ignore */
128
+ ...(ngDevMode ? [{ debugName: "globalFilterFields" }] : /* istanbul ignore next */ []));
129
+ /** Permite cambiar el ancho de las columnas arrastrando o con el teclado. */
130
+ this.resizableColumns = input(false, { ...(ngDevMode ? { debugName: "resizableColumns" } : /* istanbul ignore next */ {}), transform: toBoolean });
131
+ /** Claves (`dataKey`) o filas expandidas (two-way). */
132
+ this.expandedRowKeys = model([], /* @ts-ignore */
133
+ ...(ngDevMode ? [{ debugName: "expandedRowKeys" }] : /* istanbul ignore next */ []));
134
+ /** Alto máximo (CSS) de la tabla: hace scroll con la cabecera fija. */
135
+ this.scrollHeight = input(null, /* @ts-ignore */
136
+ ...(ngDevMode ? [{ debugName: "scrollHeight" }] : /* istanbul ignore next */ []));
137
+ /** Agrupa las filas por este campo, con una fila de cabecera por grupo. */
138
+ this.groupRowsBy = input(null, /* @ts-ignore */
139
+ ...(ngDevMode ? [{ debugName: "groupRowsBy" }] : /* istanbul ignore next */ []));
140
+ /**
141
+ * Datos desde el servidor: la tabla no filtra, ordena ni pagina; emite `lazyLoad`
142
+ * y muestra `value` tal cual (la página actual).
143
+ */
144
+ this.lazy = input(false, { ...(ngDevMode ? { debugName: "lazy" } : /* istanbul ignore next */ {}), transform: toBoolean });
145
+ /** Total de filas en el servidor (modo `lazy`), para el paginador. */
146
+ this.totalRecords = input(null, /* @ts-ignore */
147
+ ...(ngDevMode ? [{ debugName: "totalRecords" }] : /* istanbul ignore next */ []));
148
+ /** Muestra la capa de carga y marca la tabla con `aria-busy`. */
149
+ this.loading = input(false, { ...(ngDevMode ? { debugName: "loading" } : /* istanbul ignore next */ {}), transform: toBoolean });
58
150
  this.selection = model(null, /* @ts-ignore */
59
151
  ...(ngDevMode ? [{ debugName: "selection" }] : /* istanbul ignore next */ []));
60
152
  this.sortChange = output();
61
- this.first = signal(0, /* @ts-ignore */
62
- ...(ngDevMode ? [{ debugName: "first" }] : /* istanbul ignore next */ []));
153
+ this.filterChange = output();
154
+ this.columnResize = output();
155
+ this.rowExpand = output();
156
+ this.rowCollapse = output();
157
+ /** En modo `lazy`: página, orden y filtros que hay que pedir al servidor. */
158
+ this.lazyLoad = output();
63
159
  this.sortField = signal(null, /* @ts-ignore */
64
160
  ...(ngDevMode ? [{ debugName: "sortField" }] : /* istanbul ignore next */ []));
65
161
  this.sortOrder = signal(1, /* @ts-ignore */
66
162
  ...(ngDevMode ? [{ debugName: "sortOrder" }] : /* istanbul ignore next */ []));
163
+ this.columnFilters = signal({}, /* @ts-ignore */
164
+ ...(ngDevMode ? [{ debugName: "columnFilters" }] : /* istanbul ignore next */ []));
165
+ this.widths = signal({}, /* @ts-ignore */
166
+ ...(ngDevMode ? [{ debugName: "widths" }] : /* istanbul ignore next */ []));
167
+ /** Columnas en orden de pantalla: las fijas primero. */
168
+ this.displayColumns = computed(() => [
169
+ ...this.columns().filter((c) => c.frozen),
170
+ ...this.columns().filter((c) => !c.frozen),
171
+ ], /* @ts-ignore */
172
+ ...(ngDevMode ? [{ debugName: "displayColumns" }] : /* istanbul ignore next */ []));
173
+ this.hasFrozen = computed(() => this.columns().some((c) => c.frozen), /* @ts-ignore */
174
+ ...(ngDevMode ? [{ debugName: "hasFrozen" }] : /* istanbul ignore next */ []));
175
+ this.lastFrozen = computed(() => this.displayColumns().filter((c) => c.frozen).at(-1)?.field ?? null, /* @ts-ignore */
176
+ ...(ngDevMode ? [{ debugName: "lastFrozen" }] : /* istanbul ignore next */ []));
177
+ /** Desplazamiento `left` (px) de cada celda fija, medido tras el render. */
178
+ this.frozenOffsets = signal({}, /* @ts-ignore */
179
+ ...(ngDevMode ? [{ debugName: "frozenOffsets" }] : /* istanbul ignore next */ []));
180
+ this.hasColumnFilters = computed(() => this.columns().some((c) => c.filter), /* @ts-ignore */
181
+ ...(ngDevMode ? [{ debugName: "hasColumnFilters" }] : /* istanbul ignore next */ []));
182
+ this.isFiltering = computed(() => !!this.globalFilter().trim() || Object.values(this.columnFilters()).some((v) => !!v?.trim()), /* @ts-ignore */
183
+ ...(ngDevMode ? [{ debugName: "isFiltering" }] : /* istanbul ignore next */ []));
184
+ this.filtered = computed(() => {
185
+ if (this.lazy())
186
+ return this.value();
187
+ const global = normalize(this.globalFilter());
188
+ const globalFields = this.globalFilterFields() ?? this.columns().map((c) => c.field);
189
+ const columnFilters = Object.entries(this.columnFilters())
190
+ .map(([field, text]) => ({ field, text: normalize(text), mode: this.matchMode(field) }))
191
+ .filter((f) => f.text);
192
+ return this.value().filter((row) => (!global || globalFields.some((field) => matches(fieldValue(row, field), global, 'contains'))) &&
193
+ columnFilters.every((f) => matches(fieldValue(row, f.field), f.text, f.mode)));
194
+ }, /* @ts-ignore */
195
+ ...(ngDevMode ? [{ debugName: "filtered" }] : /* istanbul ignore next */ []));
67
196
  this.sorted = computed(() => {
197
+ if (this.lazy())
198
+ return this.value();
68
199
  const field = this.sortField();
69
- const data = [...this.value()];
70
- if (!field)
200
+ const group = this.groupRowsBy();
201
+ const data = [...this.filtered()];
202
+ if (!field && !group)
71
203
  return data;
72
204
  const order = this.sortOrder();
73
- return data.sort((a, b) => this.compare(a, b, field) * order);
205
+ // Con grupos, primero se ordena por el campo del grupo para que sus filas queden juntas.
206
+ return data.sort((a, b) => (group ? this.compare(a, b, group) : 0) || (field ? this.compare(a, b, field) * order : 0));
74
207
  }, /* @ts-ignore */
75
208
  ...(ngDevMode ? [{ debugName: "sorted" }] : /* istanbul ignore next */ []));
209
+ /** Filas totales: las del servidor en modo `lazy`, las filtradas si no. */
210
+ this.total = computed(() => (this.lazy() ? (this.totalRecords() ?? this.value().length) : this.sorted().length), /* @ts-ignore */
211
+ ...(ngDevMode ? [{ debugName: "total" }] : /* istanbul ignore next */ []));
212
+ /** Primera fila de la página; vuelve a 0 al filtrar u ordenar (y, sin `lazy`, al cambiar los datos). */
213
+ this.first = linkedSignal({ ...(ngDevMode ? { debugName: "first" } : /* istanbul ignore next */ {}), source: () => [
214
+ this.sortField(),
215
+ this.sortOrder(),
216
+ this.globalFilter(),
217
+ this.columnFilters(),
218
+ this.lazy() ? null : this.value(),
219
+ ],
220
+ computation: () => 0 });
221
+ /** Filas por grupo en todos los datos (no solo en la página). */
222
+ this.groupCounts = computed(() => {
223
+ const field = this.groupRowsBy();
224
+ const counts = new Map();
225
+ if (!field)
226
+ return counts;
227
+ for (const row of this.sorted()) {
228
+ const key = fieldValue(row, field);
229
+ counts.set(key, (counts.get(key) ?? 0) + 1);
230
+ }
231
+ return counts;
232
+ }, /* @ts-ignore */
233
+ ...(ngDevMode ? [{ debugName: "groupCounts" }] : /* istanbul ignore next */ []));
76
234
  this.pageRows = computed(() => {
77
235
  const data = this.sorted();
78
- if (!this.paginator())
236
+ if (!this.paginator() || this.lazy())
79
237
  return data;
80
238
  const start = Math.min(this.first(), Math.max(0, data.length - 1));
81
239
  return data.slice(start, start + this.rows());
82
240
  }, /* @ts-ignore */
83
241
  ...(ngDevMode ? [{ debugName: "pageRows" }] : /* istanbul ignore next */ []));
84
- this.colspan = computed(() => this.columns().length + (this.selectionMode() ? 1 : 0), /* @ts-ignore */
242
+ this.colspan = computed(() => this.displayColumns().length + (this.selectionMode() ? 1 : 0) + (this.expansion() ? 1 : 0), /* @ts-ignore */
85
243
  ...(ngDevMode ? [{ debugName: "colspan" }] : /* istanbul ignore next */ []));
86
- this.allSelected = computed(() => {
87
- const data = this.sorted();
88
- if (!data.length)
89
- return false;
244
+ this.selectedCount = computed(() => {
90
245
  const sel = Array.isArray(this.selection()) ? this.selection() : [];
91
- return data.every((row) => sel.some((s) => this.same(s, row)));
246
+ return this.sorted().filter((row) => sel.some((s) => this.same(s, row))).length;
92
247
  }, /* @ts-ignore */
248
+ ...(ngDevMode ? [{ debugName: "selectedCount" }] : /* istanbul ignore next */ []));
249
+ this.allSelected = computed(() => this.sorted().length > 0 && this.selectedCount() === this.sorted().length, /* @ts-ignore */
93
250
  ...(ngDevMode ? [{ debugName: "allSelected" }] : /* istanbul ignore next */ []));
251
+ this.someSelected = computed(() => this.selectedCount() > 0 && !this.allSelected(), /* @ts-ignore */
252
+ ...(ngDevMode ? [{ debugName: "someSelected" }] : /* istanbul ignore next */ []));
253
+ this.uid = `cb-table-${++nextId}`;
254
+ this.stopResize = null;
255
+ inject(DestroyRef).onDestroy(() => this.stopResize?.());
256
+ // En modo lazy, la primera página se pide al montar la tabla.
257
+ afterNextRender(() => {
258
+ if (this.lazy())
259
+ this.emitLazy();
260
+ });
261
+ // Columnas fijas: se miden los encabezados para saber dónde anclar cada una.
262
+ afterEveryRender(() => this.measureFrozen());
263
+ }
264
+ // --- Filtros ---------------------------------------------------------------
265
+ /** Aplica el filtro global (equivale a `[globalFilter]`). */
266
+ filterGlobal(text) {
267
+ this.globalFilter.set(text);
268
+ this.emitFilters();
269
+ }
270
+ /** Aplica el filtro de una columna. */
271
+ filterColumn(field, text) {
272
+ this.columnFilters.update((f) => ({ ...f, [field]: text }));
273
+ this.emitFilters();
274
+ }
275
+ /** Quita todos los filtros. */
276
+ clearFilters() {
277
+ this.globalFilter.set('');
278
+ this.columnFilters.set({});
279
+ this.emitFilters();
280
+ }
281
+ emitFilters() {
282
+ this.filterChange.emit({ global: this.globalFilter(), columns: this.columnFilters() });
283
+ this.emitLazy(0);
284
+ }
285
+ /** Pide al servidor la página actual (solo en modo `lazy`). */
286
+ emitLazy(first = this.first()) {
287
+ if (!this.lazy())
288
+ return;
289
+ this.lazyLoad.emit({
290
+ first,
291
+ rows: this.rows(),
292
+ sortField: this.sortField(),
293
+ sortOrder: this.sortOrder(),
294
+ globalFilter: this.globalFilter(),
295
+ filters: this.columnFilters(),
296
+ });
297
+ }
298
+ matchMode(field) {
299
+ return this.columns().find((c) => c.field === field)?.filterMatchMode ?? 'contains';
94
300
  }
95
301
  // --- Ordenación ------------------------------------------------------------
96
302
  onSort(col) {
@@ -103,8 +309,8 @@ class CbTable {
103
309
  this.sortField.set(col.field);
104
310
  this.sortOrder.set(1);
105
311
  }
106
- this.first.set(0);
107
312
  this.sortChange.emit({ field: this.sortField(), order: this.sortOrder() });
313
+ this.emitLazy(0);
108
314
  }
109
315
  sortIcon(col) {
110
316
  if (this.sortField() !== col.field)
@@ -138,16 +344,138 @@ class CbTable {
138
344
  const sel = this.selection();
139
345
  return Array.isArray(sel) ? sel.some((s) => this.same(s, row)) : this.same(sel, row);
140
346
  }
347
+ // --- Filas expandibles -------------------------------------------------------
348
+ isExpanded(row) {
349
+ const key = this.keyOf(row);
350
+ return this.expandedRowKeys().some((k) => k === key);
351
+ }
352
+ /** Expande o contrae una fila. */
353
+ toggleExpansion(row) {
354
+ const key = this.keyOf(row);
355
+ if (this.isExpanded(row)) {
356
+ this.expandedRowKeys.update((keys) => keys.filter((k) => k !== key));
357
+ this.rowCollapse.emit(row);
358
+ }
359
+ else {
360
+ this.expandedRowKeys.update((keys) => [...keys, key]);
361
+ this.rowExpand.emit(row);
362
+ }
363
+ }
364
+ expansionId(index) {
365
+ return `${this.uid}-expansion-${index}`;
366
+ }
367
+ // --- Columnas redimensionables ---------------------------------------------------
368
+ columnWidth(col) {
369
+ const px = this.widths()[col.field];
370
+ return px != null ? `${px}px` : col.width || null;
371
+ }
372
+ startResize(event, col) {
373
+ event.preventDefault();
374
+ event.stopPropagation();
375
+ const th = event.target.closest('th');
376
+ const startX = event.clientX;
377
+ const startWidth = this.currentWidth(th, col);
378
+ const move = (e) => this.setWidth(col.field, startWidth + e.clientX - startX);
379
+ const up = () => {
380
+ this.stopResize?.();
381
+ this.columnResize.emit({ field: col.field, width: this.widths()[col.field] ?? startWidth });
382
+ };
383
+ this.stopResize?.();
384
+ this.document.addEventListener('pointermove', move);
385
+ this.document.addEventListener('pointerup', up);
386
+ this.stopResize = () => {
387
+ this.document.removeEventListener('pointermove', move);
388
+ this.document.removeEventListener('pointerup', up);
389
+ this.stopResize = null;
390
+ };
391
+ }
392
+ /** Flecha izquierda / derecha cambian el ancho (10 px; 50 px con Mayús). */
393
+ onResizerKeydown(event, col) {
394
+ if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight')
395
+ return;
396
+ event.preventDefault();
397
+ const step = (event.shiftKey ? 50 : 10) * (event.key === 'ArrowLeft' ? -1 : 1);
398
+ const th = event.target.closest('th');
399
+ this.setWidth(col.field, this.currentWidth(th, col) + step);
400
+ this.columnResize.emit({ field: col.field, width: this.widths()[col.field] ?? MIN_COLUMN_WIDTH });
401
+ }
402
+ currentWidth(th, col) {
403
+ return this.widths()[col.field] ?? (th?.getBoundingClientRect().width || parseFloat(col.width ?? '') || 120);
404
+ }
405
+ setWidth(field, width) {
406
+ this.widths.update((w) => ({ ...w, [field]: Math.max(MIN_COLUMN_WIDTH, Math.round(width)) }));
407
+ }
141
408
  // --- Paginación ------------------------------------------------------------
142
409
  onPageChange(state) {
143
410
  this.first.set(state.first);
144
411
  this.rows.set(state.rows);
412
+ this.emitLazy(state.first);
413
+ }
414
+ // --- Grupos --------------------------------------------------------------------
415
+ groupOf(row) {
416
+ const field = this.groupRowsBy();
417
+ return field ? fieldValue(row, field) : null;
418
+ }
419
+ groupCount(row) {
420
+ return this.groupCounts().get(this.groupOf(row)) ?? 0;
421
+ }
422
+ /** La fila abre grupo si es la primera de la página o cambia el valor del grupo. */
423
+ isGroupStart(index) {
424
+ const rows = this.pageRows();
425
+ return index === 0 || this.groupOf(rows[index - 1]) !== this.groupOf(rows[index]);
426
+ }
427
+ // --- Columnas fijas ----------------------------------------------------------------
428
+ frozenLeft(key) {
429
+ return this.hasFrozen() ? (this.frozenOffsets()[key] ?? 0) : null;
430
+ }
431
+ /** Suma los anchos de las celdas fijas anteriores (expandir, selección y columnas `frozen`). */
432
+ measureFrozen() {
433
+ const table = this.tableRef()?.nativeElement;
434
+ if (!table || !this.hasFrozen())
435
+ return;
436
+ const frozen = new Set(['__exp', '__sel', ...this.columns().filter((c) => c.frozen).map((c) => c.field)]);
437
+ const offsets = {};
438
+ let left = 0;
439
+ for (const th of Array.from(table.querySelectorAll('thead tr:first-child > th[data-key]'))) {
440
+ const key = th.dataset['key'];
441
+ if (!frozen.has(key))
442
+ break;
443
+ offsets[key] = Math.round(left);
444
+ left += th.getBoundingClientRect().width;
445
+ }
446
+ const current = this.frozenOffsets();
447
+ const changed = Object.keys(offsets).some((k) => current[k] !== offsets[k]) || Object.keys(current).length !== Object.keys(offsets).length;
448
+ if (changed)
449
+ this.frozenOffsets.set(offsets);
450
+ }
451
+ // --- Exportación ---------------------------------------------------------------------
452
+ /**
453
+ * Datos en CSV: columnas visibles (salvo `exportable: false`) y todas las filas
454
+ * filtradas y ordenadas (no solo la página), o las seleccionadas con `selectionOnly`.
455
+ */
456
+ toCSV(options = {}) {
457
+ const sep = options.separator ?? ',';
458
+ const cols = this.displayColumns().filter((c) => c.exportable !== false);
459
+ const rows = options.selectionOnly ? this.sorted().filter((row) => this.isSelected(row)) : this.sorted();
460
+ const cell = (v) => {
461
+ const text = v == null ? '' : String(v);
462
+ return /["\n\r]/.test(text) || text.includes(sep) ? `"${text.replace(/"/g, '""')}"` : text;
463
+ };
464
+ return [cols.map((c) => cell(c.header)).join(sep), ...rows.map((row) => cols.map((c) => cell(fieldValue(row, c.field))).join(sep))].join('\r\n');
465
+ }
466
+ /** Descarga el CSV (`nombre.csv`), con BOM para que Excel respete tildes y eñes. */
467
+ exportCSV(filename = 'datos', options = {}) {
468
+ const blob = new Blob(['\uFEFF' + this.toCSV(options)], { type: 'text/csv;charset=utf-8' });
469
+ const url = URL.createObjectURL(blob);
470
+ const link = this.document.createElement('a');
471
+ link.href = url;
472
+ link.download = filename.endsWith('.csv') ? filename : `${filename}.csv`;
473
+ link.click();
474
+ setTimeout(() => URL.revokeObjectURL(url));
145
475
  }
146
476
  // --- Helpers ---------------------------------------------------------------
147
477
  cellValue(row, col) {
148
- return row != null && typeof row === 'object'
149
- ? row[col.field]
150
- : row;
478
+ return row != null && typeof row === 'object' ? row[col.field] : row;
151
479
  }
152
480
  cellTemplate(field) {
153
481
  return this.cellTemplates().find((cell) => cell.cbTableCell() === field);
@@ -162,6 +490,11 @@ class CbTable {
162
490
  }
163
491
  return index;
164
492
  }
493
+ /** Clave de una fila: su `dataKey` o la propia fila. */
494
+ keyOf(row) {
495
+ const key = this.dataKey();
496
+ return key && row != null && typeof row === 'object' ? row[key] : row;
497
+ }
165
498
  same(a, b) {
166
499
  if (a == null || b == null)
167
500
  return a === b;
@@ -183,193 +516,486 @@ class CbTable {
183
516
  return String(av).localeCompare(String(bv), undefined, { numeric: true });
184
517
  }
185
518
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbTable, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
186
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: CbTable, isStandalone: true, selector: "cb-table", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, paginator: { classPropertyName: "paginator", publicName: "paginator", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, rowsPerPageOptions: { classPropertyName: "rowsPerPageOptions", publicName: "rowsPerPageOptions", isSignal: true, isRequired: false, transformFunction: null }, striped: { classPropertyName: "striped", publicName: "striped", isSignal: true, isRequired: false, transformFunction: null }, rowHover: { classPropertyName: "rowHover", publicName: "rowHover", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, selectionMode: { classPropertyName: "selectionMode", publicName: "selectionMode", isSignal: true, isRequired: false, transformFunction: null }, dataKey: { classPropertyName: "dataKey", publicName: "dataKey", isSignal: true, isRequired: false, transformFunction: null }, selection: { classPropertyName: "selection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rows: "rowsChange", selection: "selectionChange", sortChange: "sortChange" }, host: { classAttribute: "cb-table-host" }, queries: [{ propertyName: "cellTemplates", predicate: CbTableCellDirective, isSignal: true }], ngImport: i0, template: `
187
- <div class="cb-table__wrap">
188
- <table class="cb-table" [class]="'cb-table--' + size()" [class.cb-table--striped]="striped()" [class.cb-table--hover]="rowHover()">
519
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: CbTable, isStandalone: true, selector: "cb-table", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, paginator: { classPropertyName: "paginator", publicName: "paginator", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, rowsPerPageOptions: { classPropertyName: "rowsPerPageOptions", publicName: "rowsPerPageOptions", isSignal: true, isRequired: false, transformFunction: null }, striped: { classPropertyName: "striped", publicName: "striped", isSignal: true, isRequired: false, transformFunction: null }, rowHover: { classPropertyName: "rowHover", publicName: "rowHover", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, selectionMode: { classPropertyName: "selectionMode", publicName: "selectionMode", isSignal: true, isRequired: false, transformFunction: null }, dataKey: { classPropertyName: "dataKey", publicName: "dataKey", isSignal: true, isRequired: false, transformFunction: null }, globalFilter: { classPropertyName: "globalFilter", publicName: "globalFilter", isSignal: true, isRequired: false, transformFunction: null }, globalFilterFields: { classPropertyName: "globalFilterFields", publicName: "globalFilterFields", isSignal: true, isRequired: false, transformFunction: null }, resizableColumns: { classPropertyName: "resizableColumns", publicName: "resizableColumns", isSignal: true, isRequired: false, transformFunction: null }, expandedRowKeys: { classPropertyName: "expandedRowKeys", publicName: "expandedRowKeys", isSignal: true, isRequired: false, transformFunction: null }, scrollHeight: { classPropertyName: "scrollHeight", publicName: "scrollHeight", isSignal: true, isRequired: false, transformFunction: null }, groupRowsBy: { classPropertyName: "groupRowsBy", publicName: "groupRowsBy", isSignal: true, isRequired: false, transformFunction: null }, lazy: { classPropertyName: "lazy", publicName: "lazy", isSignal: true, isRequired: false, transformFunction: null }, totalRecords: { classPropertyName: "totalRecords", publicName: "totalRecords", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, selection: { classPropertyName: "selection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rows: "rowsChange", globalFilter: "globalFilterChange", expandedRowKeys: "expandedRowKeysChange", selection: "selectionChange", sortChange: "sortChange", filterChange: "filterChange", columnResize: "columnResize", rowExpand: "rowExpand", rowCollapse: "rowCollapse", lazyLoad: "lazyLoad" }, host: { classAttribute: "cb-table-host" }, queries: [{ propertyName: "cellTemplates", predicate: CbTableCellDirective, isSignal: true }, { propertyName: "expansion", first: true, predicate: CbTableRowExpansion, descendants: true, isSignal: true }, { propertyName: "groupHeader", first: true, predicate: CbTableGroupHeader, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "tableRef", first: true, predicate: ["table"], descendants: true, isSignal: true }], ngImport: i0, template: `
520
+ <ng-content select="[cbTableHeader]" />
521
+ <div class="cb-table__wrap" [class.cb-table__wrap--scroll]="!!scrollHeight()" [style.max-height]="scrollHeight()">
522
+ <table
523
+ #table
524
+ class="cb-table"
525
+ [class]="'cb-table--' + size()"
526
+ [class.cb-table--striped]="striped()"
527
+ [class.cb-table--hover]="rowHover()"
528
+ [class.cb-table--resizable]="resizableColumns()"
529
+ [class.cb-table--scroll]="!!scrollHeight()"
530
+ [attr.aria-label]="ariaLabel() || null"
531
+ [attr.aria-busy]="loading() || null"
532
+ >
189
533
  <thead>
190
534
  <tr>
535
+ @if (expansion()) {
536
+ <th class="cb-table__sel" scope="col" data-key="__exp" [class.cb-is-frozen]="hasFrozen()" [style.left.px]="frozenLeft('__exp')">
537
+ <span class="cb-table__sr">{{ t().details }}</span>
538
+ </th>
539
+ }
191
540
  @if (selectionMode() === 'multiple') {
192
- <th class="cb-table__sel">
541
+ <th class="cb-table__sel" scope="col" data-key="__sel" [class.cb-is-frozen]="hasFrozen()" [style.left.px]="frozenLeft('__sel')">
193
542
  <span
194
543
  class="cb-table__check"
195
544
  [class.cb-is-checked]="allSelected()"
545
+ [class.cb-is-mixed]="someSelected()"
196
546
  role="checkbox"
197
- [attr.aria-checked]="allSelected()"
547
+ [attr.aria-checked]="allSelected() ? 'true' : someSelected() ? 'mixed' : 'false'"
548
+ [attr.aria-label]="t().selectAllRows"
198
549
  tabindex="0"
199
550
  (click)="toggleAll()"
200
- (keydown.enter)="toggleAll()"
201
551
  (keydown.space)="toggleAll(); $event.preventDefault()"
202
552
  >
203
- @if (allSelected()) { <cb-icon name="check" [size]="13" aria-hidden="true" /> }
553
+ @if (allSelected()) {
554
+ <cb-icon name="check" [size]="13" aria-hidden="true" />
555
+ } @else if (someSelected()) {
556
+ <cb-icon name="minus" [size]="13" aria-hidden="true" />
557
+ }
204
558
  </span>
205
559
  </th>
206
560
  } @else if (selectionMode() === 'single') {
207
- <th class="cb-table__sel"></th>
561
+ <th class="cb-table__sel" scope="col" data-key="__sel" [class.cb-is-frozen]="hasFrozen()" [style.left.px]="frozenLeft('__sel')">
562
+ <span class="cb-table__sr">{{ t().select }}</span>
563
+ </th>
208
564
  }
209
- @for (col of columns(); track col.field) {
565
+ @for (col of displayColumns(); track col.field) {
210
566
  <th
211
567
  class="cb-table__th"
568
+ scope="col"
569
+ [attr.data-key]="col.field"
570
+ [class.cb-is-frozen]="!!col.frozen"
571
+ [class.cb-is-frozen-last]="col.field === lastFrozen()"
572
+ [style.left.px]="col.frozen ? frozenLeft(col.field) : null"
212
573
  [class.cb-is-sortable]="col.sortable"
213
- [style.width]="col.width || null"
574
+ [style.width]="columnWidth(col)"
214
575
  [style.text-align]="col.align || null"
215
576
  [attr.aria-sort]="ariaSort(col)"
216
- (click)="onSort(col)"
217
577
  >
218
- <span class="cb-table__th-inner">
219
- {{ col.header }}
220
- @if (col.sortable) {
578
+ @if (col.sortable) {
579
+ <button
580
+ type="button"
581
+ class="cb-table__th-inner cb-table__sort-btn"
582
+ [attr.aria-label]="fmt(t().sortBy, col.header)"
583
+ (click)="onSort(col)"
584
+ >
585
+ {{ col.header }}
221
586
  <cb-icon class="cb-table__sort" [name]="sortIcon(col)" [size]="15" aria-hidden="true" />
222
- }
223
- </span>
587
+ </button>
588
+ } @else {
589
+ <span class="cb-table__th-inner">{{ col.header }}</span>
590
+ }
591
+ @if (resizableColumns()) {
592
+ <span
593
+ class="cb-table__resizer"
594
+ role="separator"
595
+ aria-orientation="vertical"
596
+ tabindex="0"
597
+ [attr.aria-label]="fmt(t().resizeColumn, col.header)"
598
+ [attr.aria-valuenow]="widths()[col.field] ?? null"
599
+ [attr.aria-valuemin]="minColumnWidth"
600
+ (pointerdown)="startResize($event, col)"
601
+ (keydown)="onResizerKeydown($event, col)"
602
+ ></span>
603
+ }
224
604
  </th>
225
605
  }
226
606
  </tr>
607
+ @if (hasColumnFilters()) {
608
+ <tr class="cb-table__filter-row">
609
+ @if (expansion()) {
610
+ <td class="cb-table__sel" [class.cb-is-frozen]="hasFrozen()" [style.left.px]="frozenLeft('__exp')"></td>
611
+ }
612
+ @if (selectionMode()) {
613
+ <td class="cb-table__sel" [class.cb-is-frozen]="hasFrozen()" [style.left.px]="frozenLeft('__sel')"></td>
614
+ }
615
+ @for (col of displayColumns(); track col.field) {
616
+ <td
617
+ class="cb-table__filter-cell"
618
+ [class.cb-is-frozen]="!!col.frozen"
619
+ [class.cb-is-frozen-last]="col.field === lastFrozen()"
620
+ [style.left.px]="col.frozen ? frozenLeft(col.field) : null"
621
+ >
622
+ @if (col.filter) {
623
+ <input
624
+ type="search"
625
+ class="cb-table__filter"
626
+ [value]="columnFilters()[col.field] ?? ''"
627
+ [placeholder]="col.filterPlaceholder ?? t().search"
628
+ [attr.aria-label]="fmt(t().filterBy, col.header)"
629
+ (input)="filterColumn(col.field, $any($event.target).value)"
630
+ />
631
+ }
632
+ </td>
633
+ }
634
+ </tr>
635
+ }
227
636
  </thead>
228
637
  <tbody>
229
- @for (row of pageRows(); track rowKey(row, $index)) {
638
+ @for (row of pageRows(); track rowKey(row, $index); let i = $index) {
639
+ @if (groupRowsBy() && isGroupStart(i)) {
640
+ <tr class="cb-table__group">
641
+ <th scope="rowgroup" [attr.colspan]="colspan()">
642
+ @if (groupHeader(); as gh) {
643
+ <ng-container
644
+ [ngTemplateOutlet]="gh.template"
645
+ [ngTemplateOutletContext]="{ $implicit: groupOf(row), count: groupCount(row) }"
646
+ />
647
+ } @else {
648
+ {{ groupOf(row) }} <span class="cb-table__group-count">({{ groupCount(row) }})</span>
649
+ }
650
+ </th>
651
+ </tr>
652
+ }
230
653
  <tr
231
654
  class="cb-table__row"
232
655
  [class.cb-is-selected]="isSelected(row)"
233
656
  [class.cb-is-selectable]="!!selectionMode()"
234
657
  (click)="onRowClick(row)"
235
658
  >
659
+ @if (expansion()) {
660
+ <td class="cb-table__sel" [class.cb-is-frozen]="hasFrozen()" [style.left.px]="frozenLeft('__exp')">
661
+ <button
662
+ type="button"
663
+ class="cb-table__expander"
664
+ [attr.aria-expanded]="isExpanded(row)"
665
+ [attr.aria-controls]="isExpanded(row) ? expansionId(first() + i) : null"
666
+ [attr.aria-label]="fmt(t().expandRow, first() + i + 1)"
667
+ (click)="toggleExpansion(row); $event.stopPropagation()"
668
+ >
669
+ <cb-icon [name]="isExpanded(row) ? 'chevron-down' : 'chevron-right'" [size]="15" aria-hidden="true" />
670
+ </button>
671
+ </td>
672
+ }
236
673
  @if (selectionMode()) {
237
- <td class="cb-table__sel">
238
- <span class="cb-table__check" [class.cb-is-checked]="isSelected(row)" aria-hidden="true">
239
- @if (isSelected(row)) { <cb-icon name="check" [size]="13" /> }
674
+ <td class="cb-table__sel" [class.cb-is-frozen]="hasFrozen()" [style.left.px]="frozenLeft('__sel')">
675
+ <span
676
+ class="cb-table__check"
677
+ [class.cb-table__check--radio]="selectionMode() === 'single'"
678
+ [class.cb-is-checked]="isSelected(row)"
679
+ [attr.role]="selectionMode() === 'single' ? 'radio' : 'checkbox'"
680
+ [attr.aria-checked]="isSelected(row)"
681
+ [attr.aria-label]="fmt(t().selectRow, first() + i + 1)"
682
+ tabindex="0"
683
+ (keydown.space)="onRowClick(row); $event.preventDefault()"
684
+ >
685
+ @if (isSelected(row)) {
686
+ <cb-icon name="check" [size]="13" aria-hidden="true" />
687
+ }
240
688
  </span>
241
689
  </td>
242
690
  }
243
- @for (col of columns(); track col.field) {
244
- <td class="cb-table__td" [style.text-align]="col.align || null">
691
+ @for (col of displayColumns(); track col.field) {
692
+ <td
693
+ class="cb-table__td"
694
+ [class.cb-is-frozen]="!!col.frozen"
695
+ [class.cb-is-frozen-last]="col.field === lastFrozen()"
696
+ [style.left.px]="col.frozen ? frozenLeft(col.field) : null"
697
+ [style.text-align]="col.align || null"
698
+ >
245
699
  @if (cellTemplate(col.field); as cell) {
246
- <ng-container
247
- [ngTemplateOutlet]="cell.template"
248
- [ngTemplateOutletContext]="cellContext(row, col)"
249
- />
700
+ <ng-container [ngTemplateOutlet]="cell.template" [ngTemplateOutletContext]="cellContext(row, col)" />
250
701
  } @else {
251
702
  {{ cellValue(row, col) }}
252
703
  }
253
704
  </td>
254
705
  }
255
706
  </tr>
707
+ @if (expansion(); as exp) {
708
+ @if (isExpanded(row)) {
709
+ <tr class="cb-table__expansion" [id]="expansionId(first() + i)">
710
+ <td [attr.colspan]="colspan()">
711
+ <ng-container
712
+ [ngTemplateOutlet]="exp.template"
713
+ [ngTemplateOutletContext]="{ $implicit: row, index: first() + i }"
714
+ />
715
+ </td>
716
+ </tr>
717
+ }
718
+ }
256
719
  } @empty {
257
720
  <tr>
258
- <td class="cb-table__empty" [attr.colspan]="colspan()">{{ emptyMessage() }}</td>
721
+ <td class="cb-table__empty" [attr.colspan]="colspan()">{{ (emptyMessage() ?? t().noData) }}</td>
259
722
  </tr>
260
723
  }
261
724
  </tbody>
262
725
  </table>
726
+ @if (loading()) {
727
+ <div class="cb-table__loading" role="status">
728
+ <cb-icon name="loader" [size]="22" [spin]="true" aria-hidden="true" />
729
+ <span class="cb-table__sr">{{ t().loading }}</span>
730
+ </div>
731
+ }
263
732
  </div>
733
+ @if (isFiltering()) {
734
+ <span class="cb-table__sr" aria-live="polite">{{ fmt(t().resultsCount, total()) }}</span>
735
+ }
264
736
 
265
737
  @if (paginator()) {
266
738
  <cb-paginator
267
739
  class="cb-table__paginator"
268
- [totalRecords]="sorted().length"
740
+ [totalRecords]="total()"
269
741
  [(first)]="first"
270
742
  [(rows)]="rows"
271
743
  [rowsPerPageOptions]="rowsPerPageOptions()"
272
744
  (pageChange)="onPageChange($event)"
273
745
  />
274
746
  }
275
- `, isInline: true, styles: [":host{display:block;font-family:var(--cb-font-sans);color:var(--cb-color-text)}.cb-table__wrap{overflow-x:auto;border:var(--cb-border-width) solid var(--cb-color-border);border-radius:var(--cb-radius-md)}.cb-table{width:100%;border-collapse:collapse;font-size:var(--cb-text-sm)}.cb-table__th,.cb-table__td,.cb-table__sel{padding:var(--cb-space-3) var(--cb-space-4);text-align:left;border-bottom:var(--cb-border-width) solid var(--cb-color-border);vertical-align:middle}.cb-table--sm .cb-table__th,.cb-table--sm .cb-table__td,.cb-table--sm .cb-table__sel{padding:var(--cb-space-2) var(--cb-space-3)}.cb-table--lg .cb-table__th,.cb-table--lg .cb-table__td,.cb-table--lg .cb-table__sel{padding:var(--cb-space-4) var(--cb-space-5)}.cb-table thead .cb-table__th,.cb-table thead .cb-table__sel{background-color:var(--cb-color-surface-50);color:var(--cb-color-text);font-weight:var(--cb-font-weight-semibold);white-space:nowrap;border-bottom-width:2px}.cb-table__th-inner{display:inline-flex;align-items:center;gap:var(--cb-space-1)}.cb-table__th.cb-is-sortable{cursor:pointer;-webkit-user-select:none;user-select:none}.cb-table__th.cb-is-sortable:hover{background-color:var(--cb-color-surface-100)}.cb-table__sort{color:var(--cb-color-text-muted)}.cb-table tbody tr:last-child .cb-table__td,.cb-table tbody tr:last-child .cb-table__sel{border-bottom:none}.cb-table--striped tbody tr:nth-child(2n){background-color:var(--cb-color-surface-50)}.cb-table--hover tbody .cb-table__row:hover{background-color:var(--cb-color-surface-100)}.cb-table__row.cb-is-selectable{cursor:pointer}.cb-table__row.cb-is-selected{background-color:var(--cb-color-primary-50)!important}.cb-table__sel{width:1px;white-space:nowrap}.cb-table__check{display:inline-flex;align-items:center;justify-content:center;width:1.1rem;height:1.1rem;border:var(--cb-border-width) solid var(--cb-color-border-strong);border-radius:var(--cb-radius-sm);color:var(--cb-color-primary-contrast);outline:none}.cb-table__check:focus-visible{box-shadow:0 0 0 3px var(--cb-color-focus-ring)}.cb-table__check.cb-is-checked{background-color:var(--cb-color-primary-500);border-color:var(--cb-color-primary-500)}.cb-table__empty{padding:var(--cb-space-6);text-align:center;color:var(--cb-color-text-muted)}.cb-table__paginator{margin-top:var(--cb-space-3)}:host-context([data-theme=\"dark\"]) .cb-table thead .cb-table__th,:host-context([data-theme=\"dark\"]) .cb-table thead .cb-table__sel{background-color:var(--cb-color-surface-100)}:host-context([data-theme=\"dark\"]) .cb-table--striped tbody tr:nth-child(2n){background-color:color-mix(in srgb,var(--cb-color-surface-200) 45%,transparent)}:host-context([data-theme=\"dark\"]) .cb-table--hover tbody .cb-table__row:hover{background-color:var(--cb-color-surface-200)}:host-context([data-theme=\"dark\"]) .cb-table__row.cb-is-selected{background-color:color-mix(in srgb,var(--cb-color-primary-500) 20%,transparent)!important}\n"], dependencies: [{ kind: "component", type: CbIcon, selector: "cb-icon", inputs: ["name", "size", "spin", "ariaLabel"] }, { kind: "component", type: CbPaginator, selector: "cb-paginator", inputs: ["totalRecords", "rows", "first", "rowsPerPageOptions", "showReport", "maxPageLinks"], outputs: ["rowsChange", "firstChange", "pageChange"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
747
+ `, isInline: true, styles: [":host{display:block;font-family:var(--cb-font-sans);color:var(--cb-color-text)}.cb-table__wrap{overflow-x:auto;border:var(--cb-border-width) solid var(--cb-color-border);border-radius:var(--cb-radius-md)}.cb-table{width:100%;border-collapse:collapse;font-size:var(--cb-text-sm)}.cb-table__th,.cb-table__td,.cb-table__sel{padding:var(--cb-space-3) var(--cb-space-4);text-align:left;border-bottom:var(--cb-border-width) solid var(--cb-color-border);vertical-align:middle}.cb-table--sm .cb-table__th,.cb-table--sm .cb-table__td,.cb-table--sm .cb-table__sel{padding:var(--cb-space-2) var(--cb-space-3)}.cb-table--lg .cb-table__th,.cb-table--lg .cb-table__td,.cb-table--lg .cb-table__sel{padding:var(--cb-space-4) var(--cb-space-5)}.cb-table thead .cb-table__th,.cb-table thead .cb-table__sel{background-color:var(--cb-color-surface-subtle);color:var(--cb-color-text);font-weight:var(--cb-font-weight-semibold);white-space:nowrap;border-bottom-width:2px}.cb-table__th-inner{display:inline-flex;align-items:center;gap:var(--cb-space-1)}.cb-table__th{position:relative}.cb-table__th.cb-is-sortable:hover{background-color:var(--cb-color-surface-hover)}.cb-table__sort-btn{margin:0;padding:0;font:inherit;color:inherit;text-align:inherit;background:none;border:none;border-radius:var(--cb-radius-sm);cursor:pointer;-webkit-user-select:none;user-select:none}.cb-table__sort-btn:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:2px}.cb-table--resizable{table-layout:fixed}.cb-table--resizable .cb-table__th{overflow:hidden;text-overflow:ellipsis}.cb-table__resizer{position:absolute;top:0;right:0;width:.5rem;height:100%;cursor:col-resize;touch-action:none}.cb-table__resizer:after{content:\"\";position:absolute;top:25%;right:.2rem;width:2px;height:50%;background-color:var(--cb-color-border-strong);border-radius:1px}.cb-table__resizer:hover:after,.cb-table__resizer:focus-visible:after{background-color:var(--cb-color-primary-500)}.cb-table__resizer:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:-2px}.cb-table__filter-row td{padding:var(--cb-space-2) var(--cb-space-4);background-color:var(--cb-color-surface-subtle);border-bottom:var(--cb-border-width) solid var(--cb-color-border)}.cb-table__filter{box-sizing:border-box;width:100%;min-width:6rem;padding:var(--cb-space-1) var(--cb-space-2);font:inherit;font-size:var(--cb-text-sm);color:var(--cb-color-text);background-color:var(--cb-color-surface-0);border:var(--cb-border-width) solid var(--cb-color-border-strong);border-radius:var(--cb-radius-sm)}.cb-table__filter:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:1px}.cb-table__expander{display:inline-flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;padding:0;color:var(--cb-color-text-muted);background:none;border:none;border-radius:var(--cb-radius-sm);cursor:pointer}.cb-table__expander:hover{background-color:var(--cb-color-surface-hover)}.cb-table__expander:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:1px}.cb-table__expansion>td{padding:var(--cb-space-4);background-color:var(--cb-color-surface-subtle);border-bottom:var(--cb-border-width) solid var(--cb-color-border)}.cb-table__sr{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}.cb-table__sort{color:var(--cb-color-text-muted)}.cb-table tbody tr:last-child .cb-table__td,.cb-table tbody tr:last-child .cb-table__sel{border-bottom:none}.cb-table--striped tbody tr:nth-child(2n){background-color:var(--cb-color-surface-subtle)}.cb-table--hover tbody .cb-table__row:hover{background-color:var(--cb-color-surface-hover)}.cb-table__row.cb-is-selectable{cursor:pointer}.cb-table__row.cb-is-selected{background-color:var(--cb-color-primary-50)!important}.cb-table__sel{width:1px;white-space:nowrap}.cb-table__check{display:inline-flex;align-items:center;justify-content:center;width:1.1rem;height:1.1rem;border:var(--cb-border-width) solid var(--cb-color-border-strong);border-radius:var(--cb-radius-sm);color:var(--cb-color-primary-contrast);outline:none}.cb-table__check:focus-visible{box-shadow:0 0 0 3px var(--cb-color-focus-ring)}.cb-table__check.cb-is-checked,.cb-table__check.cb-is-mixed{background-color:var(--cb-color-primary-500);border-color:var(--cb-color-primary-500)}.cb-table__check--radio{border-radius:50%}.cb-table__empty{padding:var(--cb-space-6);text-align:center;color:var(--cb-color-text-muted)}.cb-table__paginator{margin-top:var(--cb-space-3)}:host-context([data-theme=\"dark\"]) .cb-table__row.cb-is-selected{background-color:color-mix(in srgb,var(--cb-color-primary-500) 20%,transparent)!important}.cb-table__wrap{position:relative}.cb-table__wrap--scroll{overflow:auto}.cb-table--scroll thead{position:sticky;top:0;z-index:2}.cb-is-frozen{position:sticky;z-index:1;background-color:var(--cb-color-surface-raised)}thead .cb-is-frozen{z-index:3;background-color:var(--cb-color-surface-subtle)}.cb-table--striped tbody tr:nth-child(2n) .cb-is-frozen{background-color:var(--cb-color-surface-subtle)}.cb-table--hover tbody .cb-table__row:hover .cb-is-frozen{background-color:var(--cb-color-surface-hover)}.cb-is-frozen-last{box-shadow:4px 0 6px -4px #00000040}.cb-table__group>th{padding:var(--cb-space-2) var(--cb-space-4);font-size:var(--cb-text-sm);font-weight:var(--cb-font-weight-semibold);text-align:left;color:var(--cb-color-text-strong);background-color:var(--cb-color-surface-subtle);border-bottom:var(--cb-border-width) solid var(--cb-color-border)}.cb-table__group-count{font-weight:var(--cb-font-weight-normal, 400);color:var(--cb-color-text-muted)}.cb-table__loading{position:absolute;inset:0;z-index:4;display:flex;align-items:center;justify-content:center;color:var(--cb-color-primary-500);background-color:color-mix(in srgb,var(--cb-color-surface-raised) 65%,transparent)}\n"], dependencies: [{ kind: "component", type: CbIcon, selector: "cb-icon", inputs: ["name", "size", "spin", "ariaLabel"] }, { kind: "component", type: CbPaginator, selector: "cb-paginator", inputs: ["totalRecords", "rows", "first", "rowsPerPageOptions", "showReport", "maxPageLinks"], outputs: ["rowsChange", "firstChange", "pageChange"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
276
748
  }
277
749
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbTable, decorators: [{
278
750
  type: Component,
279
751
  args: [{ selector: 'cb-table', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, imports: [CbIcon, CbPaginator, NgTemplateOutlet], host: { class: 'cb-table-host' }, template: `
280
- <div class="cb-table__wrap">
281
- <table class="cb-table" [class]="'cb-table--' + size()" [class.cb-table--striped]="striped()" [class.cb-table--hover]="rowHover()">
752
+ <ng-content select="[cbTableHeader]" />
753
+ <div class="cb-table__wrap" [class.cb-table__wrap--scroll]="!!scrollHeight()" [style.max-height]="scrollHeight()">
754
+ <table
755
+ #table
756
+ class="cb-table"
757
+ [class]="'cb-table--' + size()"
758
+ [class.cb-table--striped]="striped()"
759
+ [class.cb-table--hover]="rowHover()"
760
+ [class.cb-table--resizable]="resizableColumns()"
761
+ [class.cb-table--scroll]="!!scrollHeight()"
762
+ [attr.aria-label]="ariaLabel() || null"
763
+ [attr.aria-busy]="loading() || null"
764
+ >
282
765
  <thead>
283
766
  <tr>
767
+ @if (expansion()) {
768
+ <th class="cb-table__sel" scope="col" data-key="__exp" [class.cb-is-frozen]="hasFrozen()" [style.left.px]="frozenLeft('__exp')">
769
+ <span class="cb-table__sr">{{ t().details }}</span>
770
+ </th>
771
+ }
284
772
  @if (selectionMode() === 'multiple') {
285
- <th class="cb-table__sel">
773
+ <th class="cb-table__sel" scope="col" data-key="__sel" [class.cb-is-frozen]="hasFrozen()" [style.left.px]="frozenLeft('__sel')">
286
774
  <span
287
775
  class="cb-table__check"
288
776
  [class.cb-is-checked]="allSelected()"
777
+ [class.cb-is-mixed]="someSelected()"
289
778
  role="checkbox"
290
- [attr.aria-checked]="allSelected()"
779
+ [attr.aria-checked]="allSelected() ? 'true' : someSelected() ? 'mixed' : 'false'"
780
+ [attr.aria-label]="t().selectAllRows"
291
781
  tabindex="0"
292
782
  (click)="toggleAll()"
293
- (keydown.enter)="toggleAll()"
294
783
  (keydown.space)="toggleAll(); $event.preventDefault()"
295
784
  >
296
- @if (allSelected()) { <cb-icon name="check" [size]="13" aria-hidden="true" /> }
785
+ @if (allSelected()) {
786
+ <cb-icon name="check" [size]="13" aria-hidden="true" />
787
+ } @else if (someSelected()) {
788
+ <cb-icon name="minus" [size]="13" aria-hidden="true" />
789
+ }
297
790
  </span>
298
791
  </th>
299
792
  } @else if (selectionMode() === 'single') {
300
- <th class="cb-table__sel"></th>
793
+ <th class="cb-table__sel" scope="col" data-key="__sel" [class.cb-is-frozen]="hasFrozen()" [style.left.px]="frozenLeft('__sel')">
794
+ <span class="cb-table__sr">{{ t().select }}</span>
795
+ </th>
301
796
  }
302
- @for (col of columns(); track col.field) {
797
+ @for (col of displayColumns(); track col.field) {
303
798
  <th
304
799
  class="cb-table__th"
800
+ scope="col"
801
+ [attr.data-key]="col.field"
802
+ [class.cb-is-frozen]="!!col.frozen"
803
+ [class.cb-is-frozen-last]="col.field === lastFrozen()"
804
+ [style.left.px]="col.frozen ? frozenLeft(col.field) : null"
305
805
  [class.cb-is-sortable]="col.sortable"
306
- [style.width]="col.width || null"
806
+ [style.width]="columnWidth(col)"
307
807
  [style.text-align]="col.align || null"
308
808
  [attr.aria-sort]="ariaSort(col)"
309
- (click)="onSort(col)"
310
809
  >
311
- <span class="cb-table__th-inner">
312
- {{ col.header }}
313
- @if (col.sortable) {
810
+ @if (col.sortable) {
811
+ <button
812
+ type="button"
813
+ class="cb-table__th-inner cb-table__sort-btn"
814
+ [attr.aria-label]="fmt(t().sortBy, col.header)"
815
+ (click)="onSort(col)"
816
+ >
817
+ {{ col.header }}
314
818
  <cb-icon class="cb-table__sort" [name]="sortIcon(col)" [size]="15" aria-hidden="true" />
315
- }
316
- </span>
819
+ </button>
820
+ } @else {
821
+ <span class="cb-table__th-inner">{{ col.header }}</span>
822
+ }
823
+ @if (resizableColumns()) {
824
+ <span
825
+ class="cb-table__resizer"
826
+ role="separator"
827
+ aria-orientation="vertical"
828
+ tabindex="0"
829
+ [attr.aria-label]="fmt(t().resizeColumn, col.header)"
830
+ [attr.aria-valuenow]="widths()[col.field] ?? null"
831
+ [attr.aria-valuemin]="minColumnWidth"
832
+ (pointerdown)="startResize($event, col)"
833
+ (keydown)="onResizerKeydown($event, col)"
834
+ ></span>
835
+ }
317
836
  </th>
318
837
  }
319
838
  </tr>
839
+ @if (hasColumnFilters()) {
840
+ <tr class="cb-table__filter-row">
841
+ @if (expansion()) {
842
+ <td class="cb-table__sel" [class.cb-is-frozen]="hasFrozen()" [style.left.px]="frozenLeft('__exp')"></td>
843
+ }
844
+ @if (selectionMode()) {
845
+ <td class="cb-table__sel" [class.cb-is-frozen]="hasFrozen()" [style.left.px]="frozenLeft('__sel')"></td>
846
+ }
847
+ @for (col of displayColumns(); track col.field) {
848
+ <td
849
+ class="cb-table__filter-cell"
850
+ [class.cb-is-frozen]="!!col.frozen"
851
+ [class.cb-is-frozen-last]="col.field === lastFrozen()"
852
+ [style.left.px]="col.frozen ? frozenLeft(col.field) : null"
853
+ >
854
+ @if (col.filter) {
855
+ <input
856
+ type="search"
857
+ class="cb-table__filter"
858
+ [value]="columnFilters()[col.field] ?? ''"
859
+ [placeholder]="col.filterPlaceholder ?? t().search"
860
+ [attr.aria-label]="fmt(t().filterBy, col.header)"
861
+ (input)="filterColumn(col.field, $any($event.target).value)"
862
+ />
863
+ }
864
+ </td>
865
+ }
866
+ </tr>
867
+ }
320
868
  </thead>
321
869
  <tbody>
322
- @for (row of pageRows(); track rowKey(row, $index)) {
870
+ @for (row of pageRows(); track rowKey(row, $index); let i = $index) {
871
+ @if (groupRowsBy() && isGroupStart(i)) {
872
+ <tr class="cb-table__group">
873
+ <th scope="rowgroup" [attr.colspan]="colspan()">
874
+ @if (groupHeader(); as gh) {
875
+ <ng-container
876
+ [ngTemplateOutlet]="gh.template"
877
+ [ngTemplateOutletContext]="{ $implicit: groupOf(row), count: groupCount(row) }"
878
+ />
879
+ } @else {
880
+ {{ groupOf(row) }} <span class="cb-table__group-count">({{ groupCount(row) }})</span>
881
+ }
882
+ </th>
883
+ </tr>
884
+ }
323
885
  <tr
324
886
  class="cb-table__row"
325
887
  [class.cb-is-selected]="isSelected(row)"
326
888
  [class.cb-is-selectable]="!!selectionMode()"
327
889
  (click)="onRowClick(row)"
328
890
  >
891
+ @if (expansion()) {
892
+ <td class="cb-table__sel" [class.cb-is-frozen]="hasFrozen()" [style.left.px]="frozenLeft('__exp')">
893
+ <button
894
+ type="button"
895
+ class="cb-table__expander"
896
+ [attr.aria-expanded]="isExpanded(row)"
897
+ [attr.aria-controls]="isExpanded(row) ? expansionId(first() + i) : null"
898
+ [attr.aria-label]="fmt(t().expandRow, first() + i + 1)"
899
+ (click)="toggleExpansion(row); $event.stopPropagation()"
900
+ >
901
+ <cb-icon [name]="isExpanded(row) ? 'chevron-down' : 'chevron-right'" [size]="15" aria-hidden="true" />
902
+ </button>
903
+ </td>
904
+ }
329
905
  @if (selectionMode()) {
330
- <td class="cb-table__sel">
331
- <span class="cb-table__check" [class.cb-is-checked]="isSelected(row)" aria-hidden="true">
332
- @if (isSelected(row)) { <cb-icon name="check" [size]="13" /> }
906
+ <td class="cb-table__sel" [class.cb-is-frozen]="hasFrozen()" [style.left.px]="frozenLeft('__sel')">
907
+ <span
908
+ class="cb-table__check"
909
+ [class.cb-table__check--radio]="selectionMode() === 'single'"
910
+ [class.cb-is-checked]="isSelected(row)"
911
+ [attr.role]="selectionMode() === 'single' ? 'radio' : 'checkbox'"
912
+ [attr.aria-checked]="isSelected(row)"
913
+ [attr.aria-label]="fmt(t().selectRow, first() + i + 1)"
914
+ tabindex="0"
915
+ (keydown.space)="onRowClick(row); $event.preventDefault()"
916
+ >
917
+ @if (isSelected(row)) {
918
+ <cb-icon name="check" [size]="13" aria-hidden="true" />
919
+ }
333
920
  </span>
334
921
  </td>
335
922
  }
336
- @for (col of columns(); track col.field) {
337
- <td class="cb-table__td" [style.text-align]="col.align || null">
923
+ @for (col of displayColumns(); track col.field) {
924
+ <td
925
+ class="cb-table__td"
926
+ [class.cb-is-frozen]="!!col.frozen"
927
+ [class.cb-is-frozen-last]="col.field === lastFrozen()"
928
+ [style.left.px]="col.frozen ? frozenLeft(col.field) : null"
929
+ [style.text-align]="col.align || null"
930
+ >
338
931
  @if (cellTemplate(col.field); as cell) {
339
- <ng-container
340
- [ngTemplateOutlet]="cell.template"
341
- [ngTemplateOutletContext]="cellContext(row, col)"
342
- />
932
+ <ng-container [ngTemplateOutlet]="cell.template" [ngTemplateOutletContext]="cellContext(row, col)" />
343
933
  } @else {
344
934
  {{ cellValue(row, col) }}
345
935
  }
346
936
  </td>
347
937
  }
348
938
  </tr>
939
+ @if (expansion(); as exp) {
940
+ @if (isExpanded(row)) {
941
+ <tr class="cb-table__expansion" [id]="expansionId(first() + i)">
942
+ <td [attr.colspan]="colspan()">
943
+ <ng-container
944
+ [ngTemplateOutlet]="exp.template"
945
+ [ngTemplateOutletContext]="{ $implicit: row, index: first() + i }"
946
+ />
947
+ </td>
948
+ </tr>
949
+ }
950
+ }
349
951
  } @empty {
350
952
  <tr>
351
- <td class="cb-table__empty" [attr.colspan]="colspan()">{{ emptyMessage() }}</td>
953
+ <td class="cb-table__empty" [attr.colspan]="colspan()">{{ (emptyMessage() ?? t().noData) }}</td>
352
954
  </tr>
353
955
  }
354
956
  </tbody>
355
957
  </table>
958
+ @if (loading()) {
959
+ <div class="cb-table__loading" role="status">
960
+ <cb-icon name="loader" [size]="22" [spin]="true" aria-hidden="true" />
961
+ <span class="cb-table__sr">{{ t().loading }}</span>
962
+ </div>
963
+ }
356
964
  </div>
965
+ @if (isFiltering()) {
966
+ <span class="cb-table__sr" aria-live="polite">{{ fmt(t().resultsCount, total()) }}</span>
967
+ }
357
968
 
358
969
  @if (paginator()) {
359
970
  <cb-paginator
360
971
  class="cb-table__paginator"
361
- [totalRecords]="sorted().length"
972
+ [totalRecords]="total()"
362
973
  [(first)]="first"
363
974
  [(rows)]="rows"
364
975
  [rowsPerPageOptions]="rowsPerPageOptions()"
365
976
  (pageChange)="onPageChange($event)"
366
977
  />
367
978
  }
368
- `, styles: [":host{display:block;font-family:var(--cb-font-sans);color:var(--cb-color-text)}.cb-table__wrap{overflow-x:auto;border:var(--cb-border-width) solid var(--cb-color-border);border-radius:var(--cb-radius-md)}.cb-table{width:100%;border-collapse:collapse;font-size:var(--cb-text-sm)}.cb-table__th,.cb-table__td,.cb-table__sel{padding:var(--cb-space-3) var(--cb-space-4);text-align:left;border-bottom:var(--cb-border-width) solid var(--cb-color-border);vertical-align:middle}.cb-table--sm .cb-table__th,.cb-table--sm .cb-table__td,.cb-table--sm .cb-table__sel{padding:var(--cb-space-2) var(--cb-space-3)}.cb-table--lg .cb-table__th,.cb-table--lg .cb-table__td,.cb-table--lg .cb-table__sel{padding:var(--cb-space-4) var(--cb-space-5)}.cb-table thead .cb-table__th,.cb-table thead .cb-table__sel{background-color:var(--cb-color-surface-50);color:var(--cb-color-text);font-weight:var(--cb-font-weight-semibold);white-space:nowrap;border-bottom-width:2px}.cb-table__th-inner{display:inline-flex;align-items:center;gap:var(--cb-space-1)}.cb-table__th.cb-is-sortable{cursor:pointer;-webkit-user-select:none;user-select:none}.cb-table__th.cb-is-sortable:hover{background-color:var(--cb-color-surface-100)}.cb-table__sort{color:var(--cb-color-text-muted)}.cb-table tbody tr:last-child .cb-table__td,.cb-table tbody tr:last-child .cb-table__sel{border-bottom:none}.cb-table--striped tbody tr:nth-child(2n){background-color:var(--cb-color-surface-50)}.cb-table--hover tbody .cb-table__row:hover{background-color:var(--cb-color-surface-100)}.cb-table__row.cb-is-selectable{cursor:pointer}.cb-table__row.cb-is-selected{background-color:var(--cb-color-primary-50)!important}.cb-table__sel{width:1px;white-space:nowrap}.cb-table__check{display:inline-flex;align-items:center;justify-content:center;width:1.1rem;height:1.1rem;border:var(--cb-border-width) solid var(--cb-color-border-strong);border-radius:var(--cb-radius-sm);color:var(--cb-color-primary-contrast);outline:none}.cb-table__check:focus-visible{box-shadow:0 0 0 3px var(--cb-color-focus-ring)}.cb-table__check.cb-is-checked{background-color:var(--cb-color-primary-500);border-color:var(--cb-color-primary-500)}.cb-table__empty{padding:var(--cb-space-6);text-align:center;color:var(--cb-color-text-muted)}.cb-table__paginator{margin-top:var(--cb-space-3)}:host-context([data-theme=\"dark\"]) .cb-table thead .cb-table__th,:host-context([data-theme=\"dark\"]) .cb-table thead .cb-table__sel{background-color:var(--cb-color-surface-100)}:host-context([data-theme=\"dark\"]) .cb-table--striped tbody tr:nth-child(2n){background-color:color-mix(in srgb,var(--cb-color-surface-200) 45%,transparent)}:host-context([data-theme=\"dark\"]) .cb-table--hover tbody .cb-table__row:hover{background-color:var(--cb-color-surface-200)}:host-context([data-theme=\"dark\"]) .cb-table__row.cb-is-selected{background-color:color-mix(in srgb,var(--cb-color-primary-500) 20%,transparent)!important}\n"] }]
369
- }], propDecorators: { cellTemplates: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => CbTableCellDirective), { isSignal: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], paginator: [{ type: i0.Input, args: [{ isSignal: true, alias: "paginator", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }, { type: i0.Output, args: ["rowsChange"] }], rowsPerPageOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowsPerPageOptions", required: false }] }], striped: [{ type: i0.Input, args: [{ isSignal: true, alias: "striped", required: false }] }], rowHover: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowHover", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], selectionMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionMode", required: false }] }], dataKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataKey", required: false }] }], selection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }, { type: i0.Output, args: ["selectionChange"] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }] } });
979
+ `, styles: [":host{display:block;font-family:var(--cb-font-sans);color:var(--cb-color-text)}.cb-table__wrap{overflow-x:auto;border:var(--cb-border-width) solid var(--cb-color-border);border-radius:var(--cb-radius-md)}.cb-table{width:100%;border-collapse:collapse;font-size:var(--cb-text-sm)}.cb-table__th,.cb-table__td,.cb-table__sel{padding:var(--cb-space-3) var(--cb-space-4);text-align:left;border-bottom:var(--cb-border-width) solid var(--cb-color-border);vertical-align:middle}.cb-table--sm .cb-table__th,.cb-table--sm .cb-table__td,.cb-table--sm .cb-table__sel{padding:var(--cb-space-2) var(--cb-space-3)}.cb-table--lg .cb-table__th,.cb-table--lg .cb-table__td,.cb-table--lg .cb-table__sel{padding:var(--cb-space-4) var(--cb-space-5)}.cb-table thead .cb-table__th,.cb-table thead .cb-table__sel{background-color:var(--cb-color-surface-subtle);color:var(--cb-color-text);font-weight:var(--cb-font-weight-semibold);white-space:nowrap;border-bottom-width:2px}.cb-table__th-inner{display:inline-flex;align-items:center;gap:var(--cb-space-1)}.cb-table__th{position:relative}.cb-table__th.cb-is-sortable:hover{background-color:var(--cb-color-surface-hover)}.cb-table__sort-btn{margin:0;padding:0;font:inherit;color:inherit;text-align:inherit;background:none;border:none;border-radius:var(--cb-radius-sm);cursor:pointer;-webkit-user-select:none;user-select:none}.cb-table__sort-btn:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:2px}.cb-table--resizable{table-layout:fixed}.cb-table--resizable .cb-table__th{overflow:hidden;text-overflow:ellipsis}.cb-table__resizer{position:absolute;top:0;right:0;width:.5rem;height:100%;cursor:col-resize;touch-action:none}.cb-table__resizer:after{content:\"\";position:absolute;top:25%;right:.2rem;width:2px;height:50%;background-color:var(--cb-color-border-strong);border-radius:1px}.cb-table__resizer:hover:after,.cb-table__resizer:focus-visible:after{background-color:var(--cb-color-primary-500)}.cb-table__resizer:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:-2px}.cb-table__filter-row td{padding:var(--cb-space-2) var(--cb-space-4);background-color:var(--cb-color-surface-subtle);border-bottom:var(--cb-border-width) solid var(--cb-color-border)}.cb-table__filter{box-sizing:border-box;width:100%;min-width:6rem;padding:var(--cb-space-1) var(--cb-space-2);font:inherit;font-size:var(--cb-text-sm);color:var(--cb-color-text);background-color:var(--cb-color-surface-0);border:var(--cb-border-width) solid var(--cb-color-border-strong);border-radius:var(--cb-radius-sm)}.cb-table__filter:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:1px}.cb-table__expander{display:inline-flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;padding:0;color:var(--cb-color-text-muted);background:none;border:none;border-radius:var(--cb-radius-sm);cursor:pointer}.cb-table__expander:hover{background-color:var(--cb-color-surface-hover)}.cb-table__expander:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:1px}.cb-table__expansion>td{padding:var(--cb-space-4);background-color:var(--cb-color-surface-subtle);border-bottom:var(--cb-border-width) solid var(--cb-color-border)}.cb-table__sr{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}.cb-table__sort{color:var(--cb-color-text-muted)}.cb-table tbody tr:last-child .cb-table__td,.cb-table tbody tr:last-child .cb-table__sel{border-bottom:none}.cb-table--striped tbody tr:nth-child(2n){background-color:var(--cb-color-surface-subtle)}.cb-table--hover tbody .cb-table__row:hover{background-color:var(--cb-color-surface-hover)}.cb-table__row.cb-is-selectable{cursor:pointer}.cb-table__row.cb-is-selected{background-color:var(--cb-color-primary-50)!important}.cb-table__sel{width:1px;white-space:nowrap}.cb-table__check{display:inline-flex;align-items:center;justify-content:center;width:1.1rem;height:1.1rem;border:var(--cb-border-width) solid var(--cb-color-border-strong);border-radius:var(--cb-radius-sm);color:var(--cb-color-primary-contrast);outline:none}.cb-table__check:focus-visible{box-shadow:0 0 0 3px var(--cb-color-focus-ring)}.cb-table__check.cb-is-checked,.cb-table__check.cb-is-mixed{background-color:var(--cb-color-primary-500);border-color:var(--cb-color-primary-500)}.cb-table__check--radio{border-radius:50%}.cb-table__empty{padding:var(--cb-space-6);text-align:center;color:var(--cb-color-text-muted)}.cb-table__paginator{margin-top:var(--cb-space-3)}:host-context([data-theme=\"dark\"]) .cb-table__row.cb-is-selected{background-color:color-mix(in srgb,var(--cb-color-primary-500) 20%,transparent)!important}.cb-table__wrap{position:relative}.cb-table__wrap--scroll{overflow:auto}.cb-table--scroll thead{position:sticky;top:0;z-index:2}.cb-is-frozen{position:sticky;z-index:1;background-color:var(--cb-color-surface-raised)}thead .cb-is-frozen{z-index:3;background-color:var(--cb-color-surface-subtle)}.cb-table--striped tbody tr:nth-child(2n) .cb-is-frozen{background-color:var(--cb-color-surface-subtle)}.cb-table--hover tbody .cb-table__row:hover .cb-is-frozen{background-color:var(--cb-color-surface-hover)}.cb-is-frozen-last{box-shadow:4px 0 6px -4px #00000040}.cb-table__group>th{padding:var(--cb-space-2) var(--cb-space-4);font-size:var(--cb-text-sm);font-weight:var(--cb-font-weight-semibold);text-align:left;color:var(--cb-color-text-strong);background-color:var(--cb-color-surface-subtle);border-bottom:var(--cb-border-width) solid var(--cb-color-border)}.cb-table__group-count{font-weight:var(--cb-font-weight-normal, 400);color:var(--cb-color-text-muted)}.cb-table__loading{position:absolute;inset:0;z-index:4;display:flex;align-items:center;justify-content:center;color:var(--cb-color-primary-500);background-color:color-mix(in srgb,var(--cb-color-surface-raised) 65%,transparent)}\n"] }]
980
+ }], ctorParameters: () => [], propDecorators: { cellTemplates: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => CbTableCellDirective), { isSignal: true }] }], expansion: [{ type: i0.ContentChild, args: [i0.forwardRef(() => CbTableRowExpansion), { isSignal: true }] }], groupHeader: [{ type: i0.ContentChild, args: [i0.forwardRef(() => CbTableGroupHeader), { isSignal: true }] }], tableRef: [{ type: i0.ViewChild, args: ['table', { isSignal: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], paginator: [{ type: i0.Input, args: [{ isSignal: true, alias: "paginator", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }, { type: i0.Output, args: ["rowsChange"] }], rowsPerPageOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowsPerPageOptions", required: false }] }], striped: [{ type: i0.Input, args: [{ isSignal: true, alias: "striped", required: false }] }], rowHover: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowHover", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], selectionMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionMode", required: false }] }], dataKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataKey", required: false }] }], globalFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "globalFilter", required: false }] }, { type: i0.Output, args: ["globalFilterChange"] }], globalFilterFields: [{ type: i0.Input, args: [{ isSignal: true, alias: "globalFilterFields", required: false }] }], resizableColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizableColumns", required: false }] }], expandedRowKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedRowKeys", required: false }] }, { type: i0.Output, args: ["expandedRowKeysChange"] }], scrollHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollHeight", required: false }] }], groupRowsBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupRowsBy", required: false }] }], lazy: [{ type: i0.Input, args: [{ isSignal: true, alias: "lazy", required: false }] }], totalRecords: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalRecords", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], selection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }, { type: i0.Output, args: ["selectionChange"] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], filterChange: [{ type: i0.Output, args: ["filterChange"] }], columnResize: [{ type: i0.Output, args: ["columnResize"] }], rowExpand: [{ type: i0.Output, args: ["rowExpand"] }], rowCollapse: [{ type: i0.Output, args: ["rowCollapse"] }], lazyLoad: [{ type: i0.Output, args: ["lazyLoad"] }] } });
370
981
  function toBoolean(value) {
371
982
  return value !== false && value != null && `${value}` !== 'false';
372
983
  }
984
+ function fieldValue(row, field) {
985
+ return row != null && typeof row === 'object' ? row[field] : row;
986
+ }
987
+ /** Minúsculas y sin tildes, para que "jose" encuentre "José". */
988
+ function normalize(text) {
989
+ return String(text ?? '')
990
+ .normalize('NFD')
991
+ .replace(/[̀-ͯ]/g, '')
992
+ .toLocaleLowerCase()
993
+ .trim();
994
+ }
995
+ function matches(value, text, mode) {
996
+ const v = normalize(value);
997
+ return mode === 'equals' ? v === text : mode === 'startsWith' ? v.startsWith(text) : v.includes(text);
998
+ }
373
999
 
374
1000
  /*
375
1001
  * Public API Surface de @caribeng/ui/table
@@ -379,4 +1005,4 @@ function toBoolean(value) {
379
1005
  * Generated bundle index. Do not edit.
380
1006
  */
381
1007
 
382
- export { CbTable, CbTableCellDirective };
1008
+ export { CbTable, CbTableCellDirective, CbTableGroupHeader, CbTableRowExpansion };