@cdevhub/ngx-tw 0.5.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/README.md +78 -15
  2. package/fesm2022/cdevhub-ngx-tw-accordion.mjs +16 -5
  3. package/fesm2022/cdevhub-ngx-tw-accordion.mjs.map +1 -1
  4. package/fesm2022/cdevhub-ngx-tw-avatar.mjs +26 -15
  5. package/fesm2022/cdevhub-ngx-tw-avatar.mjs.map +1 -1
  6. package/fesm2022/cdevhub-ngx-tw-badge.mjs +17 -5
  7. package/fesm2022/cdevhub-ngx-tw-badge.mjs.map +1 -1
  8. package/fesm2022/cdevhub-ngx-tw-breadcrumbs.mjs +27 -6
  9. package/fesm2022/cdevhub-ngx-tw-breadcrumbs.mjs.map +1 -1
  10. package/fesm2022/cdevhub-ngx-tw-button.mjs +23 -11
  11. package/fesm2022/cdevhub-ngx-tw-button.mjs.map +1 -1
  12. package/fesm2022/cdevhub-ngx-tw-calendar-luxon.mjs +5 -5
  13. package/fesm2022/cdevhub-ngx-tw-calendar-luxon.mjs.map +1 -1
  14. package/fesm2022/cdevhub-ngx-tw-calendar-testing.mjs +3 -3
  15. package/fesm2022/cdevhub-ngx-tw-calendar-testing.mjs.map +1 -1
  16. package/fesm2022/cdevhub-ngx-tw-calendar.mjs +427 -122
  17. package/fesm2022/cdevhub-ngx-tw-calendar.mjs.map +1 -1
  18. package/fesm2022/cdevhub-ngx-tw-card.mjs +21 -11
  19. package/fesm2022/cdevhub-ngx-tw-card.mjs.map +1 -1
  20. package/fesm2022/cdevhub-ngx-tw-carousel.mjs +215 -96
  21. package/fesm2022/cdevhub-ngx-tw-carousel.mjs.map +1 -1
  22. package/fesm2022/cdevhub-ngx-tw-checkbox.mjs +108 -64
  23. package/fesm2022/cdevhub-ngx-tw-checkbox.mjs.map +1 -1
  24. package/fesm2022/cdevhub-ngx-tw-code-block.mjs +18 -6
  25. package/fesm2022/cdevhub-ngx-tw-code-block.mjs.map +1 -1
  26. package/fesm2022/cdevhub-ngx-tw-collapsible.mjs +55 -30
  27. package/fesm2022/cdevhub-ngx-tw-collapsible.mjs.map +1 -1
  28. package/fesm2022/cdevhub-ngx-tw-combobox-testing.mjs +218 -0
  29. package/fesm2022/cdevhub-ngx-tw-combobox-testing.mjs.map +1 -0
  30. package/fesm2022/cdevhub-ngx-tw-combobox.mjs +236 -211
  31. package/fesm2022/cdevhub-ngx-tw-combobox.mjs.map +1 -1
  32. package/fesm2022/cdevhub-ngx-tw-command-palette-testing.mjs +200 -0
  33. package/fesm2022/cdevhub-ngx-tw-command-palette-testing.mjs.map +1 -0
  34. package/fesm2022/cdevhub-ngx-tw-command-palette.mjs +34 -7
  35. package/fesm2022/cdevhub-ngx-tw-command-palette.mjs.map +1 -1
  36. package/fesm2022/cdevhub-ngx-tw-core.mjs +135 -15
  37. package/fesm2022/cdevhub-ngx-tw-core.mjs.map +1 -1
  38. package/fesm2022/cdevhub-ngx-tw-date-picker-testing.mjs +203 -0
  39. package/fesm2022/cdevhub-ngx-tw-date-picker-testing.mjs.map +1 -0
  40. package/fesm2022/cdevhub-ngx-tw-date-picker.mjs +160 -102
  41. package/fesm2022/cdevhub-ngx-tw-date-picker.mjs.map +1 -1
  42. package/fesm2022/cdevhub-ngx-tw-date-range-picker-testing.mjs +209 -0
  43. package/fesm2022/cdevhub-ngx-tw-date-range-picker-testing.mjs.map +1 -0
  44. package/fesm2022/cdevhub-ngx-tw-date-range-picker.mjs +182 -107
  45. package/fesm2022/cdevhub-ngx-tw-date-range-picker.mjs.map +1 -1
  46. package/fesm2022/cdevhub-ngx-tw-dialog-testing.mjs +122 -0
  47. package/fesm2022/cdevhub-ngx-tw-dialog-testing.mjs.map +1 -0
  48. package/fesm2022/cdevhub-ngx-tw-dialog.mjs +11 -2
  49. package/fesm2022/cdevhub-ngx-tw-dialog.mjs.map +1 -1
  50. package/fesm2022/cdevhub-ngx-tw-empty-state.mjs +1 -1
  51. package/fesm2022/cdevhub-ngx-tw-empty-state.mjs.map +1 -1
  52. package/fesm2022/cdevhub-ngx-tw-file-upload-testing.mjs +149 -0
  53. package/fesm2022/cdevhub-ngx-tw-file-upload-testing.mjs.map +1 -0
  54. package/fesm2022/cdevhub-ngx-tw-file-upload.mjs +57 -67
  55. package/fesm2022/cdevhub-ngx-tw-file-upload.mjs.map +1 -1
  56. package/fesm2022/cdevhub-ngx-tw-flip-card.mjs +42 -24
  57. package/fesm2022/cdevhub-ngx-tw-flip-card.mjs.map +1 -1
  58. package/fesm2022/cdevhub-ngx-tw-form-field.mjs +114 -21
  59. package/fesm2022/cdevhub-ngx-tw-form-field.mjs.map +1 -1
  60. package/fesm2022/cdevhub-ngx-tw-icon.mjs +16 -6
  61. package/fesm2022/cdevhub-ngx-tw-icon.mjs.map +1 -1
  62. package/fesm2022/cdevhub-ngx-tw-input.mjs +52 -61
  63. package/fesm2022/cdevhub-ngx-tw-input.mjs.map +1 -1
  64. package/fesm2022/cdevhub-ngx-tw-item.mjs +28 -4
  65. package/fesm2022/cdevhub-ngx-tw-item.mjs.map +1 -1
  66. package/fesm2022/cdevhub-ngx-tw-menu-testing.mjs +168 -0
  67. package/fesm2022/cdevhub-ngx-tw-menu-testing.mjs.map +1 -0
  68. package/fesm2022/cdevhub-ngx-tw-menu.mjs +58 -21
  69. package/fesm2022/cdevhub-ngx-tw-menu.mjs.map +1 -1
  70. package/fesm2022/cdevhub-ngx-tw-number-input.mjs +34 -1
  71. package/fesm2022/cdevhub-ngx-tw-number-input.mjs.map +1 -1
  72. package/fesm2022/cdevhub-ngx-tw-paginator.mjs +95 -45
  73. package/fesm2022/cdevhub-ngx-tw-paginator.mjs.map +1 -1
  74. package/fesm2022/cdevhub-ngx-tw-popover.mjs +81 -32
  75. package/fesm2022/cdevhub-ngx-tw-popover.mjs.map +1 -1
  76. package/fesm2022/cdevhub-ngx-tw-progress-bar.mjs +1 -1
  77. package/fesm2022/cdevhub-ngx-tw-progress-bar.mjs.map +1 -1
  78. package/fesm2022/cdevhub-ngx-tw-radio.mjs +131 -98
  79. package/fesm2022/cdevhub-ngx-tw-radio.mjs.map +1 -1
  80. package/fesm2022/cdevhub-ngx-tw-segmented-control.mjs +207 -42
  81. package/fesm2022/cdevhub-ngx-tw-segmented-control.mjs.map +1 -1
  82. package/fesm2022/cdevhub-ngx-tw-select-testing.mjs +165 -0
  83. package/fesm2022/cdevhub-ngx-tw-select-testing.mjs.map +1 -0
  84. package/fesm2022/cdevhub-ngx-tw-select.mjs +386 -210
  85. package/fesm2022/cdevhub-ngx-tw-select.mjs.map +1 -1
  86. package/fesm2022/{cdevhub-ngx-tw-sheet-sheet-renderer-ByyNluUd.mjs → cdevhub-ngx-tw-sheet-sheet-renderer-CqVJwgmp.mjs} +3 -3
  87. package/fesm2022/cdevhub-ngx-tw-sheet-sheet-renderer-CqVJwgmp.mjs.map +1 -0
  88. package/fesm2022/cdevhub-ngx-tw-sheet-testing.mjs +129 -0
  89. package/fesm2022/cdevhub-ngx-tw-sheet-testing.mjs.map +1 -0
  90. package/fesm2022/cdevhub-ngx-tw-sheet.mjs +23 -7
  91. package/fesm2022/cdevhub-ngx-tw-sheet.mjs.map +1 -1
  92. package/fesm2022/cdevhub-ngx-tw-slider.mjs +89 -54
  93. package/fesm2022/cdevhub-ngx-tw-slider.mjs.map +1 -1
  94. package/fesm2022/cdevhub-ngx-tw-sort.mjs +122 -17
  95. package/fesm2022/cdevhub-ngx-tw-sort.mjs.map +1 -1
  96. package/fesm2022/cdevhub-ngx-tw-split.mjs +72 -19
  97. package/fesm2022/cdevhub-ngx-tw-split.mjs.map +1 -1
  98. package/fesm2022/cdevhub-ngx-tw-stat.mjs +63 -25
  99. package/fesm2022/cdevhub-ngx-tw-stat.mjs.map +1 -1
  100. package/fesm2022/cdevhub-ngx-tw-stepper.mjs +35 -8
  101. package/fesm2022/cdevhub-ngx-tw-stepper.mjs.map +1 -1
  102. package/fesm2022/cdevhub-ngx-tw-switch.mjs +54 -48
  103. package/fesm2022/cdevhub-ngx-tw-switch.mjs.map +1 -1
  104. package/fesm2022/cdevhub-ngx-tw-tab-nav.mjs +12 -15
  105. package/fesm2022/cdevhub-ngx-tw-tab-nav.mjs.map +1 -1
  106. package/fesm2022/cdevhub-ngx-tw-table.mjs +74 -13
  107. package/fesm2022/cdevhub-ngx-tw-table.mjs.map +1 -1
  108. package/fesm2022/cdevhub-ngx-tw-tabs.mjs +84 -30
  109. package/fesm2022/cdevhub-ngx-tw-tabs.mjs.map +1 -1
  110. package/fesm2022/cdevhub-ngx-tw-tags-input-testing.mjs +172 -0
  111. package/fesm2022/cdevhub-ngx-tw-tags-input-testing.mjs.map +1 -0
  112. package/fesm2022/cdevhub-ngx-tw-tags-input.mjs +107 -71
  113. package/fesm2022/cdevhub-ngx-tw-tags-input.mjs.map +1 -1
  114. package/fesm2022/cdevhub-ngx-tw-textarea.mjs +41 -9
  115. package/fesm2022/cdevhub-ngx-tw-textarea.mjs.map +1 -1
  116. package/fesm2022/cdevhub-ngx-tw-theme.mjs +257 -45
  117. package/fesm2022/cdevhub-ngx-tw-theme.mjs.map +1 -1
  118. package/fesm2022/cdevhub-ngx-tw-time-picker-testing.mjs +201 -0
  119. package/fesm2022/cdevhub-ngx-tw-time-picker-testing.mjs.map +1 -0
  120. package/fesm2022/cdevhub-ngx-tw-time-picker.mjs +184 -77
  121. package/fesm2022/cdevhub-ngx-tw-time-picker.mjs.map +1 -1
  122. package/fesm2022/cdevhub-ngx-tw-timeline.mjs +110 -27
  123. package/fesm2022/cdevhub-ngx-tw-timeline.mjs.map +1 -1
  124. package/fesm2022/{cdevhub-ngx-tw-toast-toast-renderer-DSu4YoTy.mjs → cdevhub-ngx-tw-toast-toast-renderer-BlDxemsU.mjs} +107 -19
  125. package/fesm2022/cdevhub-ngx-tw-toast-toast-renderer-BlDxemsU.mjs.map +1 -0
  126. package/fesm2022/cdevhub-ngx-tw-toast.mjs +21 -13
  127. package/fesm2022/cdevhub-ngx-tw-toast.mjs.map +1 -1
  128. package/fesm2022/cdevhub-ngx-tw-tooltip.mjs +102 -15
  129. package/fesm2022/cdevhub-ngx-tw-tooltip.mjs.map +1 -1
  130. package/fesm2022/cdevhub-ngx-tw-transfer-testing.mjs +219 -0
  131. package/fesm2022/cdevhub-ngx-tw-transfer-testing.mjs.map +1 -0
  132. package/fesm2022/cdevhub-ngx-tw-transfer.mjs +88 -69
  133. package/fesm2022/cdevhub-ngx-tw-transfer.mjs.map +1 -1
  134. package/fesm2022/cdevhub-ngx-tw-tree.mjs +68 -19
  135. package/fesm2022/cdevhub-ngx-tw-tree.mjs.map +1 -1
  136. package/index.json +1 -1
  137. package/package.json +63 -2
  138. package/theme/_base.css +13 -0
  139. package/theme/_dark.css +111 -36
  140. package/theme/_high-contrast-dark.css +367 -0
  141. package/theme/_high-contrast.css +13 -2
  142. package/theme/_light.css +332 -0
  143. package/theme/_semantic.css +60 -26
  144. package/theme/_typography.css +17 -0
  145. package/theme/index.css +86 -7
  146. package/types/cdevhub-ngx-tw-accordion.d.ts +12 -4
  147. package/types/cdevhub-ngx-tw-avatar.d.ts +10 -3
  148. package/types/cdevhub-ngx-tw-button.d.ts +1 -0
  149. package/types/cdevhub-ngx-tw-calendar-luxon.d.ts +2 -2
  150. package/types/cdevhub-ngx-tw-calendar-testing.d.ts +1 -1
  151. package/types/cdevhub-ngx-tw-calendar.d.ts +179 -73
  152. package/types/cdevhub-ngx-tw-card.d.ts +11 -3
  153. package/types/cdevhub-ngx-tw-carousel.d.ts +89 -20
  154. package/types/cdevhub-ngx-tw-checkbox.d.ts +40 -12
  155. package/types/cdevhub-ngx-tw-code-block.d.ts +11 -2
  156. package/types/cdevhub-ngx-tw-collapsible.d.ts +32 -5
  157. package/types/cdevhub-ngx-tw-combobox-testing.d.ts +157 -0
  158. package/types/cdevhub-ngx-tw-combobox.d.ts +94 -53
  159. package/types/cdevhub-ngx-tw-command-palette-testing.d.ts +136 -0
  160. package/types/cdevhub-ngx-tw-command-palette.d.ts +33 -5
  161. package/types/cdevhub-ngx-tw-core.d.ts +152 -21
  162. package/types/cdevhub-ngx-tw-date-picker-testing.d.ts +141 -0
  163. package/types/cdevhub-ngx-tw-date-picker.d.ts +37 -29
  164. package/types/cdevhub-ngx-tw-date-range-picker-testing.d.ts +148 -0
  165. package/types/cdevhub-ngx-tw-date-range-picker.d.ts +36 -29
  166. package/types/cdevhub-ngx-tw-dialog-testing.d.ts +98 -0
  167. package/types/cdevhub-ngx-tw-dialog.d.ts +12 -3
  168. package/types/cdevhub-ngx-tw-file-upload-testing.d.ts +120 -0
  169. package/types/cdevhub-ngx-tw-file-upload.d.ts +8 -10
  170. package/types/cdevhub-ngx-tw-flip-card.d.ts +18 -11
  171. package/types/cdevhub-ngx-tw-form-field.d.ts +19 -2
  172. package/types/cdevhub-ngx-tw-icon.d.ts +3 -3
  173. package/types/cdevhub-ngx-tw-input.d.ts +5 -8
  174. package/types/cdevhub-ngx-tw-item.d.ts +2 -0
  175. package/types/cdevhub-ngx-tw-menu-testing.d.ts +131 -0
  176. package/types/cdevhub-ngx-tw-menu.d.ts +2 -2
  177. package/types/cdevhub-ngx-tw-number-input.d.ts +20 -0
  178. package/types/cdevhub-ngx-tw-paginator.d.ts +58 -24
  179. package/types/cdevhub-ngx-tw-popover.d.ts +41 -10
  180. package/types/cdevhub-ngx-tw-progress-bar.d.ts +1 -1
  181. package/types/cdevhub-ngx-tw-radio.d.ts +68 -25
  182. package/types/cdevhub-ngx-tw-segmented-control.d.ts +79 -16
  183. package/types/cdevhub-ngx-tw-select-testing.d.ts +114 -0
  184. package/types/cdevhub-ngx-tw-select.d.ts +104 -33
  185. package/types/cdevhub-ngx-tw-sheet-testing.d.ts +104 -0
  186. package/types/cdevhub-ngx-tw-sheet.d.ts +19 -3
  187. package/types/cdevhub-ngx-tw-slider.d.ts +79 -25
  188. package/types/cdevhub-ngx-tw-sort.d.ts +45 -4
  189. package/types/cdevhub-ngx-tw-split.d.ts +32 -2
  190. package/types/cdevhub-ngx-tw-stat.d.ts +14 -4
  191. package/types/cdevhub-ngx-tw-stepper.d.ts +16 -2
  192. package/types/cdevhub-ngx-tw-switch.d.ts +35 -13
  193. package/types/cdevhub-ngx-tw-tab-nav.d.ts +7 -6
  194. package/types/cdevhub-ngx-tw-table.d.ts +53 -10
  195. package/types/cdevhub-ngx-tw-tabs.d.ts +41 -10
  196. package/types/cdevhub-ngx-tw-tags-input-testing.d.ts +135 -0
  197. package/types/cdevhub-ngx-tw-tags-input.d.ts +26 -11
  198. package/types/cdevhub-ngx-tw-textarea.d.ts +3 -2
  199. package/types/cdevhub-ngx-tw-theme.d.ts +245 -39
  200. package/types/cdevhub-ngx-tw-time-picker-testing.d.ts +129 -0
  201. package/types/cdevhub-ngx-tw-time-picker.d.ts +76 -19
  202. package/types/cdevhub-ngx-tw-timeline.d.ts +36 -12
  203. package/types/cdevhub-ngx-tw-toast.d.ts +20 -6
  204. package/types/cdevhub-ngx-tw-tooltip.d.ts +28 -6
  205. package/types/cdevhub-ngx-tw-transfer-testing.d.ts +152 -0
  206. package/types/cdevhub-ngx-tw-transfer.d.ts +21 -8
  207. package/types/cdevhub-ngx-tw-tree.d.ts +6 -1
  208. package/fesm2022/cdevhub-ngx-tw-sheet-sheet-renderer-ByyNluUd.mjs.map +0 -1
  209. package/fesm2022/cdevhub-ngx-tw-toast-toast-renderer-DSu4YoTy.mjs.map +0 -1
@@ -71,6 +71,7 @@ const DEFAULT_TABLE_LABELS = {
71
71
  expandRowLabel: 'Expand row',
72
72
  collapseRowLabel: 'Collapse row',
73
73
  selectAllLabel: 'Select all rows',
74
+ selectionColumnLabel: 'Selection',
74
75
  selectRowLabel: 'Select row {index}',
75
76
  };
76
77
  // ── tv() config ──────────────────────────────────────────────────────
@@ -106,6 +107,7 @@ const tableVariants = tv({
106
107
  paginationSlot: 'empty:hidden border-t border-border px-2 py-2',
107
108
  expansionRow: 'bg-surface-sunken',
108
109
  expansionCell: 'p-0',
110
+ dataRow: '',
109
111
  // The leading `_selection` column hosts checkboxes only — override the standard
110
112
  // text alignment + padding so the control stays centered in a narrow column.
111
113
  selectionHeader: 'w-12 text-center align-middle',
@@ -172,6 +174,16 @@ const tableVariants = tv({
172
174
  true: { table: '[&>tbody]:opacity-60 [&>tbody]:pointer-events-none' },
173
175
  false: {},
174
176
  },
177
+ // Data rows only become a keyboard target when the consumer opts in via
178
+ // `clickableRows`. A static table must not put every row in the tab
179
+ // order, so the affordance and the focus ring are gated on the same flag
180
+ // that adds `tabindex` and the keydown handler.
181
+ clickableRows: {
182
+ true: {
183
+ dataRow: 'cursor-pointer focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-500',
184
+ },
185
+ false: {},
186
+ },
175
187
  },
176
188
  compoundVariants: [
177
189
  {
@@ -195,6 +207,7 @@ const tableVariants = tv({
195
207
  stickyHeader: false,
196
208
  stickyFooter: false,
197
209
  loading: false,
210
+ clickableRows: false,
198
211
  },
199
212
  }, { twMerge: true });
200
213
  // ── Static class maps (Tailwind v4 JIT requires literal class strings) ──
@@ -329,7 +342,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
329
342
  }] });
330
343
  /** Structural directive on an `<ng-template>` declaring the row-expansion template. Requires `[multiTemplateRows]="true"` on the parent `<tw-table>`. */
331
344
  class RowExpansionDirective {
332
- /** Optional predicate that decides whether this expansion renders for a given row. Defaults to `undefined` (render for every expanded row). */
345
+ /** Optional predicate that decides whether this expansion renders for a given row. Defaults to `undefined` (render for every expanded row). Alias: `twRowExpansionWhen`. */
333
346
  predicate = input(undefined, { ...(ngDevMode ? { debugName: "predicate" } : /* istanbul ignore next */ {}), alias: 'twRowExpansionWhen' });
334
347
  /** @internal */
335
348
  template = inject((TemplateRef));
@@ -565,18 +578,26 @@ class TableComponent {
565
578
  /** Whether multiple row templates may render per data object. Required for `*twRowExpansion` and advanced `*twRowDef [when]` usage. Defaults to `false`. */
566
579
  multiTemplateRows = input(false, /* @ts-ignore */
567
580
  ...(ngDevMode ? [{ debugName: "multiTemplateRows" }] : /* istanbul ignore next */ []));
581
+ /**
582
+ * Whether data rows are activatable. Set this whenever you bind `(rowClicked)` — it is what puts
583
+ * each row in the tab order, wires Enter / Space to the same handler as a click, and paints the
584
+ * focus ring. Left `false`, `(rowClicked)` stays pointer-only, which keeps a static table out of
585
+ * the tab order. Defaults to `false`.
586
+ */
587
+ clickableRows = input(false, /* @ts-ignore */
588
+ ...(ngDevMode ? [{ debugName: "clickableRows" }] : /* istanbul ignore next */ []));
568
589
  /** Overrides for user-facing strings. Unset keys fall back to the English defaults. Defaults to `{}`. */
569
590
  labels = input({}, /* @ts-ignore */
570
591
  ...(ngDevMode ? [{ debugName: "labels" }] : /* istanbul ignore next */ []));
571
- /** Accessible name for the `<table>`. Required when no visible `<caption slot="caption">` is provided. */
592
+ /** Accessible name for the `<table>`. Required when no visible `<caption slot="caption">` is provided. Defaults to `undefined`. Alias: `aria-label`. */
572
593
  ariaLabel = input(undefined, { ...(ngDevMode ? { debugName: "ariaLabel" } : /* istanbul ignore next */ {}), alias: 'aria-label' });
573
- /** Id of an external element labelling the table. Mirrored to `aria-labelledby` on the `<table>`. */
594
+ /** Id of an external element labelling the table. Mirrored to `aria-labelledby` on the `<table>`. Defaults to `undefined`. */
574
595
  ariaLabelledby = input(undefined, { ...(ngDevMode ? { debugName: "ariaLabelledby" } : /* istanbul ignore next */ {}), alias: 'aria-labelledby' });
575
596
  // ── Models (two-way) ──
576
- /** Two-way bound set of rows currently expanded. Immutable — set a new `Set` on every change; do not mutate in place. */
597
+ /** Two-way bound set of rows currently expanded. Immutable — set a new `Set` on every change; do not mutate in place. Defaults to an empty set. */
577
598
  expandedRows = model(new Set(), /* @ts-ignore */
578
599
  ...(ngDevMode ? [{ debugName: "expandedRows" }] : /* istanbul ignore next */ []));
579
- /** Two-way bound list of selected rows. Set a new array on every change; do not mutate. Only used when `selection.enabled` is `true`. */
600
+ /** Two-way bound list of selected rows. Set a new array on every change; do not mutate. Only used when `selection.enabled` is `true`. Defaults to an empty array. */
580
601
  selected = model([], /* @ts-ignore */
581
602
  ...(ngDevMode ? [{ debugName: "selected" }] : /* istanbul ignore next */ []));
582
603
  // ── Outputs ──
@@ -653,11 +674,19 @@ class TableComponent {
653
674
  /** @internal Whether the error overlay should render. */
654
675
  shouldRenderErrorOverlay = computed(() => this.error() !== null, /* @ts-ignore */
655
676
  ...(ngDevMode ? [{ debugName: "shouldRenderErrorOverlay" }] : /* istanbul ignore next */ []));
656
- /** @internal Resolved table labels (defaults merged with input overrides). */
657
- resolvedLabels = computed(() => ({
658
- ...DEFAULT_TABLE_LABELS,
659
- ...this.labels(),
660
- }), /* @ts-ignore */
677
+ /**
678
+ * @internal Resolved table labels (defaults merged with input overrides).
679
+ *
680
+ * Explicitly-undefined keys are dropped before merging: a plain spread lets
681
+ * `{ selectionColumnLabel: undefined }` overwrite the default with undefined,
682
+ * which renders an empty `sr-only` span and reinstates the
683
+ * `empty-table-header` failure the label exists to prevent. `Required<>`
684
+ * then guarantees the template a string for every label.
685
+ */
686
+ resolvedLabels = computed(() => {
687
+ const overrides = Object.fromEntries(Object.entries(this.labels() ?? {}).filter(([, value]) => value !== undefined));
688
+ return { ...DEFAULT_TABLE_LABELS, ...overrides };
689
+ }, /* @ts-ignore */
661
690
  ...(ngDevMode ? [{ debugName: "resolvedLabels" }] : /* istanbul ignore next */ []));
662
691
  /** @internal Computed error message for the default error overlay. Empty when no error is set. */
663
692
  errorMessage = computed(() => {
@@ -710,6 +739,7 @@ class TableComponent {
710
739
  stickyHeader: stickyCfg.header,
711
740
  stickyFooter: stickyCfg.footer,
712
741
  loading: this.loading(),
742
+ clickableRows: this.clickableRows(),
713
743
  });
714
744
  }, /* @ts-ignore */
715
745
  ...(ngDevMode ? [{ debugName: "_variantResult" }] : /* istanbul ignore next */ []));
@@ -765,6 +795,8 @@ class TableComponent {
765
795
  ...(ngDevMode ? [{ debugName: "footerSlotClasses" }] : /* istanbul ignore next */ []));
766
796
  paginationSlotClasses = computed(() => this._variantResult().paginationSlot(), /* @ts-ignore */
767
797
  ...(ngDevMode ? [{ debugName: "paginationSlotClasses" }] : /* istanbul ignore next */ []));
798
+ dataRowClasses = computed(() => this._variantResult().dataRow(), /* @ts-ignore */
799
+ ...(ngDevMode ? [{ debugName: "dataRowClasses" }] : /* istanbul ignore next */ []));
768
800
  expansionRowClasses = computed(() => this._variantResult().expansionRow(), /* @ts-ignore */
769
801
  ...(ngDevMode ? [{ debugName: "expansionRowClasses" }] : /* istanbul ignore next */ []));
770
802
  expansionCellClasses = computed(() => this._variantResult().expansionCell(), /* @ts-ignore */
@@ -894,6 +926,13 @@ class TableComponent {
894
926
  });
895
927
  // Loading announcement.
896
928
  //
929
+ // This is the *only* announcement channel for the loading state. The
930
+ // loading overlay in table.html carries no `role="status"` / `aria-live`
931
+ // on purpose — it used to, and screen readers announced the same string
932
+ // twice per fetch. A pre-existing live region receiving text (what
933
+ // `LiveAnnouncer` does) is also the reliable pattern; an `@if`-inserted
934
+ // region whose content arrives in the same tick is not.
935
+ //
897
936
  // The effect tracks `this.loading()` only; reads of labels / rows are wrapped
898
937
  // in `untracked()` so unrelated upstream signals (label updates, row mutations
899
938
  // that don't toggle loading) don't re-announce. The announcement should fire
@@ -931,6 +970,28 @@ class TableComponent {
931
970
  }
932
971
  this.rowClicked.emit({ row, index, event });
933
972
  }
973
+ /**
974
+ * @internal Keyboard counterpart to `handleRowClick`. Only reachable when `clickableRows` is
975
+ * `true`, because that is the only state in which the `<tr>` carries `tabindex`.
976
+ *
977
+ * The `target !== currentTarget` guard is what keeps Enter on a button *inside* a cell from also
978
+ * activating the row: that keystroke's target is the button, and its own click already bubbles
979
+ * into `handleRowClick`, which suppresses it. Reusing the `composedPath()` walk here would let
980
+ * the row fire a second time.
981
+ */
982
+ handleRowKeydown(row, index, event) {
983
+ if (!this.clickableRows())
984
+ return;
985
+ if (event.target !== event.currentTarget)
986
+ return;
987
+ if (event.key !== 'Enter' && event.key !== ' ')
988
+ return;
989
+ event.preventDefault();
990
+ this.rowClicked.emit({ row, index, event });
991
+ }
992
+ /** @internal `tabindex` for a data row — present only while rows are activatable. */
993
+ rowTabIndex = computed(() => (this.clickableRows() ? 0 : null), /* @ts-ignore */
994
+ ...(ngDevMode ? [{ debugName: "rowTabIndex" }] : /* istanbul ignore next */ []));
934
995
  /** @internal Whether a row is currently expanded. */
935
996
  isRowExpanded(row) {
936
997
  const expanded = this.expandedRows();
@@ -1125,14 +1186,14 @@ class TableComponent {
1125
1186
  return formatLabel(this.resolvedLabels().selectRowLabel, { index: rowIndex + 1 });
1126
1187
  }
1127
1188
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: TableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1128
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: TableComponent, isStandalone: true, selector: "tw-table", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, trackBy: { classPropertyName: "trackBy", publicName: "trackBy", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, appearance: { classPropertyName: "appearance", publicName: "appearance", isSignal: true, isRequired: false, transformFunction: null }, sticky: { classPropertyName: "sticky", publicName: "sticky", isSignal: true, isRequired: false, transformFunction: null }, responsive: { classPropertyName: "responsive", publicName: "responsive", isSignal: true, isRequired: false, transformFunction: null }, selection: { classPropertyName: "selection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, multiTemplateRows: { classPropertyName: "multiTemplateRows", publicName: "multiTemplateRows", isSignal: true, isRequired: false, transformFunction: null }, labels: { classPropertyName: "labels", publicName: "labels", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledby: { classPropertyName: "ariaLabelledby", publicName: "aria-labelledby", isSignal: true, isRequired: false, transformFunction: null }, expandedRows: { classPropertyName: "expandedRows", publicName: "expandedRows", isSignal: true, isRequired: false, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { expandedRows: "expandedRowsChange", selected: "selectedChange", rowClicked: "rowClicked", selectionChange: "selectionChange", expansionChange: "expansionChange" }, host: { properties: { "class": "hostClasses()" } }, queries: [{ propertyName: "columns", predicate: (ColumnComponent), descendants: true, isSignal: true }, { propertyName: "noDataRow", first: true, predicate: NoDataRowDirective, descendants: true, isSignal: true }, { propertyName: "expansionTemplate", first: true, predicate: (RowExpansionDirective), descendants: true, isSignal: true }], viewQueries: [{ propertyName: "cdkTable", first: true, predicate: ["cdkTable"], descendants: true, isSignal: true }], exportAs: ["twTable"], ngImport: i0, template: "<!-- Toolbar slot \u2014 hidden when empty via `empty:hidden` in the slot class. -->\n<div [class]=\"toolbarClasses()\">\n <ng-content select=\"[slot='toolbar']\" />\n</div>\n\n<!-- Scroll wrapper \u2014 also hosts the loading overlay as an absolutely-positioned child. -->\n<div [class]=\"scrollWrapperClasses()\">\n <div [class]=\"scrollContainerClasses()\" [style.max-height]=\"maxHeightStyle()\">\n <table\n #cdkTable=\"cdkTable\"\n cdk-table\n [class]=\"tableClasses()\"\n [attr.aria-label]=\"ariaLabel() ?? null\"\n [attr.aria-labelledby]=\"ariaLabelledby() ?? null\"\n >\n <ng-content select=\"[slot='caption']\" />\n\n <!--\n Emit one <ng-container cdkColumnDef> per visible column. CdkColumnDef's\n @ContentChildren(CdkCellDef / CdkHeaderCellDef / CdkFooterCellDef)\n resolves the <th *cdkHeaderCellDef> / <td *cdkCellDef> / <td *cdkFooterCellDef>\n below \u2014 those renderers delegate to the consumer's projected templates\n (col.cellDef() / col.headerCellDef() / col.footerCellDef()) via\n ngTemplateOutlet.\n -->\n @for (col of visibleColumns(); track col.name()) {\n <ng-container\n [cdkColumnDef]=\"col.name()\"\n [sticky]=\"col.stickyStart()\"\n [stickyEnd]=\"col.stickyEnd()\"\n >\n <th\n cdk-header-cell\n *cdkHeaderCellDef\n [class]=\"col.headerCellClasses()\"\n [style.width]=\"col.resolvedWidth()\"\n [attr.aria-sort]=\"col.ariaSort()\"\n [attr.data-column]=\"col.name()\"\n scope=\"col\"\n >\n @if (col.headerCellDef(); as hdef) {\n <ng-container\n [ngTemplateOutlet]=\"hdef.template\"\n [ngTemplateOutletContext]=\"col.headerContext()\"\n />\n } @else if (col.headerLabel()) {\n {{ col.headerLabel() }}\n }\n </th>\n <td\n cdk-cell\n *cdkCellDef=\"let row; let i = index; let c = count; let f = first; let l = last; let e = even; let o = odd\"\n [class]=\"col.cellClasses()\"\n [style.width]=\"col.resolvedWidth()\"\n [attr.data-label]=\"col.stackDataLabel()\"\n [attr.data-column]=\"col.name()\"\n >\n @if (col.cellDef(); as cdef) {\n <ng-container\n [ngTemplateOutlet]=\"cdef.template\"\n [ngTemplateOutletContext]=\"col.buildCellContext(row, i, c, f, l, e, o)\"\n />\n }\n </td>\n @if (col.footerCellDef()) {\n <td\n cdk-footer-cell\n *cdkFooterCellDef\n [class]=\"col.footerCellClasses()\"\n [style.width]=\"col.resolvedWidth()\"\n [attr.data-column]=\"col.name()\"\n >\n <ng-container\n [ngTemplateOutlet]=\"col.footerCellDef()!.template\"\n [ngTemplateOutletContext]=\"col.footerContext()\"\n />\n </td>\n }\n </ng-container>\n }\n\n <!-- Selection pseudo-column emitted as a leading column when `selection.enabled` is true.\n Hosts the master \"select all\" checkbox in the header and per-row checkboxes in the body.\n The role=\"checkbox\" host on <tw-checkbox> is allowlisted by the row-click suppression\n helper so clicking it does NOT also emit `rowClicked`. -->\n @if (resolvedSelection().enabled) {\n <ng-container cdkColumnDef=\"_selection\">\n <th\n cdk-header-cell\n *cdkHeaderCellDef\n [class]=\"selectionHeaderClasses()\"\n scope=\"col\"\n >\n <!-- Select-all needs a full row snapshot, which only the array\n data-source path can produce. Rendering it for an Observable /\n DataSource input gave a checkbox that looked functional, never\n reflected state, and did nothing on click. -->\n @if (dataIsArray()) {\n <tw-checkbox\n size=\"sm\"\n [checked]=\"masterChecked()\"\n [indeterminate]=\"masterIndeterminate()\"\n (change)=\"toggleMasterSelection($event)\"\n [aria-label]=\"resolvedLabels().selectAllLabel\"\n />\n }\n </th>\n <td\n cdk-cell\n *cdkCellDef=\"let row; let i = index\"\n [class]=\"selectionCellClasses()\"\n >\n <tw-checkbox\n size=\"sm\"\n [checked]=\"isSelected(row)\"\n (change)=\"setSelected(row, $event)\"\n [aria-label]=\"selectRowLabel(i)\"\n />\n </td>\n </ng-container>\n }\n\n <!-- Auto-generated row defs driven by visibleColumnNames(). -->\n @if (visibleColumnNames().length > 0) {\n <tr\n cdk-header-row\n *cdkHeaderRowDef=\"visibleColumnNames(); sticky: stickyHeader()\"\n ></tr>\n <tr\n cdk-row\n *cdkRowDef=\"let row; let i = dataIndex; columns: visibleColumnNames()\"\n (click)=\"handleRowClick(row, i, $event)\"\n [attr.aria-selected]=\"rowAriaSelected(row)\"\n ></tr>\n @if (hasFooterRow()) {\n <tr\n cdk-footer-row\n *cdkFooterRowDef=\"visibleColumnNames(); sticky: stickyFooter()\"\n ></tr>\n }\n }\n\n <!-- Expansion pseudo-column + extra row def. -->\n @if (hasExpansion()) {\n <ng-container cdkColumnDef=\"_tw_expansion\">\n <td\n cdk-cell\n *cdkCellDef=\"let row; let i = dataIndex\"\n [attr.colspan]=\"expansionColspan()\"\n [class]=\"expansionCellClasses()\"\n >\n @if (expansionTemplate(); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl.template\"\n [ngTemplateOutletContext]=\"expansionContext(row, i)\"\n />\n }\n </td>\n </ng-container>\n <tr\n cdk-row\n *cdkRowDef=\"let row; columns: ['_tw_expansion']; when: expansionWhen\"\n [class]=\"expansionRowClasses()\"\n ></tr>\n }\n\n <!-- Consumer-projected content: tw-column metadata components and ng-template-based\n directives (*twNoDataRow, *twRowExpansion). The tw-column hosts are display:none\n so they are harmless inside the <table>. -->\n <ng-content />\n </table>\n\n <!-- Empty-state fallback overlay (hidden when a *twNoDataRow is projected; hidden when loading/error). -->\n @if (shouldRenderEmptyOverlay()) {\n <div [class]=\"emptyStateClasses()\">\n <ng-content select=\"[slot='empty']\">\n <svg\n [class]=\"emptyIconClasses()\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"M2.25 13.5h3.86a2.25 2.25 0 0 1 2.012 1.244l.256.512a2.25 2.25 0 0 0 2.013 1.244h3.218a2.25 2.25 0 0 0 2.013-1.244l.256-.512a2.25 2.25 0 0 1 2.013-1.244h3.859m-19.5.338V18a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18v-4.162c0-.224-.034-.447-.1-.661l-2.4-8.022A2.25 2.25 0 0 0 17.128 3.5H6.873a2.25 2.25 0 0 0-2.122 1.655l-2.4 8.022a2.25 2.25 0 0 0-.1.661Z\"\n />\n </svg>\n <p [class]=\"emptyMessageClasses()\">{{ resolvedLabels().empty }}</p>\n </ng-content>\n </div>\n }\n\n <!-- Error-state overlay. -->\n @if (shouldRenderErrorOverlay()) {\n <div [class]=\"errorStateClasses()\" role=\"alert\">\n <ng-content select=\"[slot='error']\">\n <svg\n [class]=\"errorIconClasses()\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z\"\n />\n </svg>\n <p [class]=\"errorMessageClasses()\">{{ errorMessage() }}</p>\n </ng-content>\n </div>\n }\n\n <!-- Loading overlay (absolute-positioned inside scroll wrapper). -->\n @if (loading()) {\n <div\n [class]=\"loadingOverlayClasses()\"\n role=\"status\"\n aria-live=\"polite\"\n animate.enter=\"fade-in\"\n animate.leave=\"fade-out\"\n >\n <ng-content select=\"[slot='loading']\">\n <svg\n class=\"size-5 shrink-0 animate-spin motion-reduce:animate-none text-fg-muted\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n aria-hidden=\"true\"\n >\n <circle\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n opacity=\"0.2\"\n />\n <circle\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n stroke-dasharray=\"60\"\n stroke-dashoffset=\"40\"\n />\n </svg>\n <span [class]=\"loadingMessageClasses()\">{{ resolvedLabels().loading }}</span>\n </ng-content>\n </div>\n }\n </div>\n</div>\n\n<!-- Footer slot \u2014 hidden when empty via `empty:hidden` in the slot class. -->\n<div [class]=\"footerSlotClasses()\">\n <ng-content select=\"[slot='footer']\" />\n</div>\n\n<!-- Pagination slot \u2014 hidden when empty via `empty:hidden` in the slot class. -->\n<div [class]=\"paginationSlotClasses()\">\n <ng-content select=\"[slot='pagination']\" />\n</div>\n", dependencies: [{ kind: "ngmodule", type: CdkTableModule }, { kind: "component", type: i1.CdkTable, selector: "cdk-table, table[cdk-table]", inputs: ["trackBy", "dataSource", "multiTemplateDataRows", "fixedLayout", "recycleRows"], outputs: ["contentChanged"], exportAs: ["cdkTable"] }, { kind: "directive", type: i1.CdkRowDef, selector: "[cdkRowDef]", inputs: ["cdkRowDefColumns", "cdkRowDefWhen"] }, { kind: "directive", type: i1.CdkCellDef, selector: "[cdkCellDef]" }, { kind: "directive", type: i1.CdkHeaderCellDef, selector: "[cdkHeaderCellDef]" }, { kind: "directive", type: i1.CdkFooterCellDef, selector: "[cdkFooterCellDef]" }, { kind: "directive", type: i1.CdkColumnDef, selector: "[cdkColumnDef]", inputs: ["cdkColumnDef", "sticky", "stickyEnd"] }, { kind: "directive", type: i1.CdkCell, selector: "cdk-cell, td[cdk-cell]" }, { kind: "component", type: i1.CdkRow, selector: "cdk-row, tr[cdk-row]" }, { kind: "directive", type: i1.CdkHeaderCell, selector: "cdk-header-cell, th[cdk-header-cell]" }, { kind: "directive", type: i1.CdkFooterCell, selector: "cdk-footer-cell, td[cdk-footer-cell]" }, { kind: "component", type: i1.CdkHeaderRow, selector: "cdk-header-row, tr[cdk-header-row]" }, { kind: "directive", type: i1.CdkHeaderRowDef, selector: "[cdkHeaderRowDef]", inputs: ["cdkHeaderRowDef", "cdkHeaderRowDefSticky"] }, { kind: "component", type: i1.CdkFooterRow, selector: "cdk-footer-row, tr[cdk-footer-row]" }, { kind: "directive", type: i1.CdkFooterRowDef, selector: "[cdkFooterRowDef]", inputs: ["cdkFooterRowDef", "cdkFooterRowDefSticky"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: CheckboxComponent, selector: "tw-checkbox", inputs: ["color", "size", "variant", "disabled", "required", "label", "description", "labelPosition", "name", "id", "aria-label", "aria-labelledby", "aria-describedby", "errorStateMatcher", "checked", "indeterminate"], outputs: ["checkedChange", "indeterminateChange", "change"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1189
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: TableComponent, isStandalone: true, selector: "tw-table", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, trackBy: { classPropertyName: "trackBy", publicName: "trackBy", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, appearance: { classPropertyName: "appearance", publicName: "appearance", isSignal: true, isRequired: false, transformFunction: null }, sticky: { classPropertyName: "sticky", publicName: "sticky", isSignal: true, isRequired: false, transformFunction: null }, responsive: { classPropertyName: "responsive", publicName: "responsive", isSignal: true, isRequired: false, transformFunction: null }, selection: { classPropertyName: "selection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, multiTemplateRows: { classPropertyName: "multiTemplateRows", publicName: "multiTemplateRows", isSignal: true, isRequired: false, transformFunction: null }, clickableRows: { classPropertyName: "clickableRows", publicName: "clickableRows", isSignal: true, isRequired: false, transformFunction: null }, labels: { classPropertyName: "labels", publicName: "labels", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledby: { classPropertyName: "ariaLabelledby", publicName: "aria-labelledby", isSignal: true, isRequired: false, transformFunction: null }, expandedRows: { classPropertyName: "expandedRows", publicName: "expandedRows", isSignal: true, isRequired: false, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { expandedRows: "expandedRowsChange", selected: "selectedChange", rowClicked: "rowClicked", selectionChange: "selectionChange", expansionChange: "expansionChange" }, host: { properties: { "class": "hostClasses()" } }, queries: [{ propertyName: "columns", predicate: (ColumnComponent), descendants: true, isSignal: true }, { propertyName: "noDataRow", first: true, predicate: NoDataRowDirective, descendants: true, isSignal: true }, { propertyName: "expansionTemplate", first: true, predicate: (RowExpansionDirective), descendants: true, isSignal: true }], viewQueries: [{ propertyName: "cdkTable", first: true, predicate: ["cdkTable"], descendants: true, isSignal: true }], exportAs: ["twTable"], ngImport: i0, template: "<!-- Toolbar slot \u2014 hidden when empty via `empty:hidden` in the slot class. -->\n<div [class]=\"toolbarClasses()\">\n <ng-content select=\"[slot='toolbar']\" />\n</div>\n\n<!-- Scroll wrapper \u2014 also hosts the loading overlay as an absolutely-positioned child. -->\n<div [class]=\"scrollWrapperClasses()\">\n <div [class]=\"scrollContainerClasses()\" [style.max-height]=\"maxHeightStyle()\">\n <table\n #cdkTable=\"cdkTable\"\n cdk-table\n [class]=\"tableClasses()\"\n [attr.aria-label]=\"ariaLabel() ?? null\"\n [attr.aria-labelledby]=\"ariaLabelledby() ?? null\"\n >\n <ng-content select=\"[slot='caption']\" />\n\n <!--\n Emit one <ng-container cdkColumnDef> per visible column. CdkColumnDef's\n @ContentChildren(CdkCellDef / CdkHeaderCellDef / CdkFooterCellDef)\n resolves the <th *cdkHeaderCellDef> / <td *cdkCellDef> / <td *cdkFooterCellDef>\n below \u2014 those renderers delegate to the consumer's projected templates\n (col.cellDef() / col.headerCellDef() / col.footerCellDef()) via\n ngTemplateOutlet.\n -->\n @for (col of visibleColumns(); track col.name()) {\n <ng-container\n [cdkColumnDef]=\"col.name()\"\n [sticky]=\"col.stickyStart()\"\n [stickyEnd]=\"col.stickyEnd()\"\n >\n <th\n cdk-header-cell\n *cdkHeaderCellDef\n [class]=\"col.headerCellClasses()\"\n [style.width]=\"col.resolvedWidth()\"\n [attr.aria-sort]=\"col.ariaSort()\"\n [attr.data-column]=\"col.name()\"\n scope=\"col\"\n >\n @if (col.headerCellDef(); as hdef) {\n <ng-container\n [ngTemplateOutlet]=\"hdef.template\"\n [ngTemplateOutletContext]=\"col.headerContext()\"\n />\n } @else if (col.headerLabel()) {\n {{ col.headerLabel() }}\n }\n </th>\n <td\n cdk-cell\n *cdkCellDef=\"let row; let i = index; let c = count; let f = first; let l = last; let e = even; let o = odd\"\n [class]=\"col.cellClasses()\"\n [style.width]=\"col.resolvedWidth()\"\n [attr.data-label]=\"col.stackDataLabel()\"\n [attr.data-column]=\"col.name()\"\n >\n @if (col.cellDef(); as cdef) {\n <ng-container\n [ngTemplateOutlet]=\"cdef.template\"\n [ngTemplateOutletContext]=\"col.buildCellContext(row, i, c, f, l, e, o)\"\n />\n }\n </td>\n @if (col.footerCellDef()) {\n <td\n cdk-footer-cell\n *cdkFooterCellDef\n [class]=\"col.footerCellClasses()\"\n [style.width]=\"col.resolvedWidth()\"\n [attr.data-column]=\"col.name()\"\n >\n <ng-container\n [ngTemplateOutlet]=\"col.footerCellDef()!.template\"\n [ngTemplateOutletContext]=\"col.footerContext()\"\n />\n </td>\n }\n </ng-container>\n }\n\n <!-- Selection pseudo-column emitted as a leading column when `selection.enabled` is true.\n Hosts the master \"select all\" checkbox in the header and per-row checkboxes in the body.\n The role=\"checkbox\" host on <tw-checkbox> is allowlisted by the row-click suppression\n helper so clicking it does NOT also emit `rowClicked`. -->\n @if (resolvedSelection().enabled) {\n <ng-container cdkColumnDef=\"_selection\">\n <th\n cdk-header-cell\n *cdkHeaderCellDef\n [class]=\"selectionHeaderClasses()\"\n scope=\"col\"\n >\n <!-- Select-all needs a full row snapshot, which only the array\n data-source path can produce. Rendering it for an Observable /\n DataSource input gave a checkbox that looked functional, never\n reflected state, and did nothing on click. -->\n @if (dataIsArray()) {\n <tw-checkbox\n size=\"sm\"\n [checked]=\"masterChecked()\"\n [indeterminate]=\"masterIndeterminate()\"\n (change)=\"toggleMasterSelection($event)\"\n [aria-label]=\"resolvedLabels().selectAllLabel\"\n />\n } @else {\n <!-- Without the master checkbox this header cell would be\n literally empty, which axe reports as `empty-table-header`\n and which leaves the column unnamed in a screen reader's\n table summary. -->\n <span class=\"sr-only\">{{ resolvedLabels().selectionColumnLabel }}</span>\n }\n </th>\n <td\n cdk-cell\n *cdkCellDef=\"let row; let i = index\"\n [class]=\"selectionCellClasses()\"\n >\n <tw-checkbox\n size=\"sm\"\n [checked]=\"isSelected(row)\"\n (change)=\"setSelected(row, $event)\"\n [aria-label]=\"selectRowLabel(i)\"\n />\n </td>\n </ng-container>\n }\n\n <!-- Auto-generated row defs driven by visibleColumnNames(). -->\n @if (visibleColumnNames().length > 0) {\n <tr\n cdk-header-row\n *cdkHeaderRowDef=\"visibleColumnNames(); sticky: stickyHeader()\"\n ></tr>\n <!-- Both index context variables are captured on purpose. CDK's\n `CdkTable` sets `dataIndex` ONLY when `multiTemplateDataRows` is\n enabled (row expansion); otherwise it sets `index` and leaves\n `dataIndex` undefined. Reading just one gives `undefined` in the\n other mode \u2014 which shipped as `rowClicked` emitting\n `index: undefined` for every table without row expansion. -->\n <!-- `tabindex` / `(keydown)` / the focus ring are all gated on\n `clickableRows`. A static table must not put every row in the tab\n order, and Angular gives no way to ask whether `(rowClicked)` has a\n subscriber \u2014 so the consumer declares it. Deliberately NO `role`\n override: a `<tr>` is a `row`, and retagging it `button` breaks the\n table's structural role contract for its own cells. -->\n <tr\n cdk-row\n *cdkRowDef=\"\n let row;\n let i = index;\n let di = dataIndex;\n columns: visibleColumnNames()\n \"\n [class]=\"dataRowClasses()\"\n (click)=\"handleRowClick(row, di ?? i, $event)\"\n (keydown)=\"handleRowKeydown(row, di ?? i, $event)\"\n [attr.tabindex]=\"rowTabIndex()\"\n [attr.aria-selected]=\"rowAriaSelected(row)\"\n ></tr>\n @if (hasFooterRow()) {\n <tr\n cdk-footer-row\n *cdkFooterRowDef=\"visibleColumnNames(); sticky: stickyFooter()\"\n ></tr>\n }\n }\n\n <!-- Expansion pseudo-column + extra row def. -->\n @if (hasExpansion()) {\n <ng-container cdkColumnDef=\"_tw_expansion\">\n <td\n cdk-cell\n *cdkCellDef=\"let row; let i = dataIndex\"\n [attr.colspan]=\"expansionColspan()\"\n [class]=\"expansionCellClasses()\"\n >\n @if (expansionTemplate(); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl.template\"\n [ngTemplateOutletContext]=\"expansionContext(row, i)\"\n />\n }\n </td>\n </ng-container>\n <tr\n cdk-row\n *cdkRowDef=\"let row; columns: ['_tw_expansion']; when: expansionWhen\"\n [class]=\"expansionRowClasses()\"\n ></tr>\n }\n\n <!-- Consumer-projected content: tw-column metadata components and ng-template-based\n directives (*twNoDataRow, *twRowExpansion). The tw-column hosts are display:none\n so they are harmless inside the <table>. -->\n <ng-content />\n </table>\n\n <!-- Empty-state fallback overlay (hidden when a *twNoDataRow is projected; hidden when loading/error). -->\n @if (shouldRenderEmptyOverlay()) {\n <div [class]=\"emptyStateClasses()\">\n <ng-content select=\"[slot='empty']\">\n <svg\n [class]=\"emptyIconClasses()\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"M2.25 13.5h3.86a2.25 2.25 0 0 1 2.012 1.244l.256.512a2.25 2.25 0 0 0 2.013 1.244h3.218a2.25 2.25 0 0 0 2.013-1.244l.256-.512a2.25 2.25 0 0 1 2.013-1.244h3.859m-19.5.338V18a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18v-4.162c0-.224-.034-.447-.1-.661l-2.4-8.022A2.25 2.25 0 0 0 17.128 3.5H6.873a2.25 2.25 0 0 0-2.122 1.655l-2.4 8.022a2.25 2.25 0 0 0-.1.661Z\"\n />\n </svg>\n <p [class]=\"emptyMessageClasses()\">{{ resolvedLabels().empty }}</p>\n </ng-content>\n </div>\n }\n\n <!-- Error-state overlay. -->\n @if (shouldRenderErrorOverlay()) {\n <div [class]=\"errorStateClasses()\" role=\"alert\">\n <ng-content select=\"[slot='error']\">\n <svg\n [class]=\"errorIconClasses()\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z\"\n />\n </svg>\n <p [class]=\"errorMessageClasses()\">{{ errorMessage() }}</p>\n </ng-content>\n </div>\n }\n\n <!--\n Loading overlay (absolute-positioned inside scroll wrapper).\n\n Deliberately NOT a live region. The loading string is announced by\n `LiveAnnouncer` from the effect in table.ts; carrying `role=\"status\"`\n (which implies `aria-live=\"polite\"`) here as well made screen readers\n announce the same text twice on every fetch. The visible text stays \u2014\n it is the sighted user's loading affordance \u2014 it just no longer\n announces itself.\n -->\n @if (loading()) {\n <div\n [class]=\"loadingOverlayClasses()\"\n data-tw-table-loading\n animate.enter=\"fade-in\"\n animate.leave=\"fade-out\"\n >\n <ng-content select=\"[slot='loading']\">\n <svg\n class=\"size-5 shrink-0 animate-spin motion-reduce:animate-none text-fg-muted\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n aria-hidden=\"true\"\n >\n <circle\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n opacity=\"0.2\"\n />\n <circle\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n stroke-dasharray=\"60\"\n stroke-dashoffset=\"40\"\n />\n </svg>\n <span [class]=\"loadingMessageClasses()\">{{ resolvedLabels().loading }}</span>\n </ng-content>\n </div>\n }\n </div>\n</div>\n\n<!-- Footer slot \u2014 hidden when empty via `empty:hidden` in the slot class. -->\n<div [class]=\"footerSlotClasses()\">\n <ng-content select=\"[slot='footer']\" />\n</div>\n\n<!-- Pagination slot \u2014 hidden when empty via `empty:hidden` in the slot class. -->\n<div [class]=\"paginationSlotClasses()\">\n <ng-content select=\"[slot='pagination']\" />\n</div>\n", dependencies: [{ kind: "ngmodule", type: CdkTableModule }, { kind: "component", type: i1.CdkTable, selector: "cdk-table, table[cdk-table]", inputs: ["trackBy", "dataSource", "multiTemplateDataRows", "fixedLayout", "recycleRows"], outputs: ["contentChanged"], exportAs: ["cdkTable"] }, { kind: "directive", type: i1.CdkRowDef, selector: "[cdkRowDef]", inputs: ["cdkRowDefColumns", "cdkRowDefWhen"] }, { kind: "directive", type: i1.CdkCellDef, selector: "[cdkCellDef]" }, { kind: "directive", type: i1.CdkHeaderCellDef, selector: "[cdkHeaderCellDef]" }, { kind: "directive", type: i1.CdkFooterCellDef, selector: "[cdkFooterCellDef]" }, { kind: "directive", type: i1.CdkColumnDef, selector: "[cdkColumnDef]", inputs: ["cdkColumnDef", "sticky", "stickyEnd"] }, { kind: "directive", type: i1.CdkCell, selector: "cdk-cell, td[cdk-cell]" }, { kind: "component", type: i1.CdkRow, selector: "cdk-row, tr[cdk-row]" }, { kind: "directive", type: i1.CdkHeaderCell, selector: "cdk-header-cell, th[cdk-header-cell]" }, { kind: "directive", type: i1.CdkFooterCell, selector: "cdk-footer-cell, td[cdk-footer-cell]" }, { kind: "component", type: i1.CdkHeaderRow, selector: "cdk-header-row, tr[cdk-header-row]" }, { kind: "directive", type: i1.CdkHeaderRowDef, selector: "[cdkHeaderRowDef]", inputs: ["cdkHeaderRowDef", "cdkHeaderRowDefSticky"] }, { kind: "component", type: i1.CdkFooterRow, selector: "cdk-footer-row, tr[cdk-footer-row]" }, { kind: "directive", type: i1.CdkFooterRowDef, selector: "[cdkFooterRowDef]", inputs: ["cdkFooterRowDef", "cdkFooterRowDefSticky"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: CheckboxComponent, selector: "tw-checkbox", inputs: ["color", "size", "variant", "disabled", "required", "label", "description", "labelPosition", "name", "id", "aria-label", "aria-labelledby", "aria-describedby", "errorStateMatcher", "checked", "indeterminate"], outputs: ["checkedChange", "indeterminateChange", "change"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1129
1190
  }
1130
1191
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: TableComponent, decorators: [{
1131
1192
  type: Component,
1132
1193
  args: [{ selector: 'tw-table', exportAs: 'twTable', changeDetection: ChangeDetectionStrategy.OnPush, imports: [CdkTableModule, NgTemplateOutlet, CheckboxComponent], host: {
1133
1194
  '[class]': 'hostClasses()',
1134
- }, template: "<!-- Toolbar slot \u2014 hidden when empty via `empty:hidden` in the slot class. -->\n<div [class]=\"toolbarClasses()\">\n <ng-content select=\"[slot='toolbar']\" />\n</div>\n\n<!-- Scroll wrapper \u2014 also hosts the loading overlay as an absolutely-positioned child. -->\n<div [class]=\"scrollWrapperClasses()\">\n <div [class]=\"scrollContainerClasses()\" [style.max-height]=\"maxHeightStyle()\">\n <table\n #cdkTable=\"cdkTable\"\n cdk-table\n [class]=\"tableClasses()\"\n [attr.aria-label]=\"ariaLabel() ?? null\"\n [attr.aria-labelledby]=\"ariaLabelledby() ?? null\"\n >\n <ng-content select=\"[slot='caption']\" />\n\n <!--\n Emit one <ng-container cdkColumnDef> per visible column. CdkColumnDef's\n @ContentChildren(CdkCellDef / CdkHeaderCellDef / CdkFooterCellDef)\n resolves the <th *cdkHeaderCellDef> / <td *cdkCellDef> / <td *cdkFooterCellDef>\n below \u2014 those renderers delegate to the consumer's projected templates\n (col.cellDef() / col.headerCellDef() / col.footerCellDef()) via\n ngTemplateOutlet.\n -->\n @for (col of visibleColumns(); track col.name()) {\n <ng-container\n [cdkColumnDef]=\"col.name()\"\n [sticky]=\"col.stickyStart()\"\n [stickyEnd]=\"col.stickyEnd()\"\n >\n <th\n cdk-header-cell\n *cdkHeaderCellDef\n [class]=\"col.headerCellClasses()\"\n [style.width]=\"col.resolvedWidth()\"\n [attr.aria-sort]=\"col.ariaSort()\"\n [attr.data-column]=\"col.name()\"\n scope=\"col\"\n >\n @if (col.headerCellDef(); as hdef) {\n <ng-container\n [ngTemplateOutlet]=\"hdef.template\"\n [ngTemplateOutletContext]=\"col.headerContext()\"\n />\n } @else if (col.headerLabel()) {\n {{ col.headerLabel() }}\n }\n </th>\n <td\n cdk-cell\n *cdkCellDef=\"let row; let i = index; let c = count; let f = first; let l = last; let e = even; let o = odd\"\n [class]=\"col.cellClasses()\"\n [style.width]=\"col.resolvedWidth()\"\n [attr.data-label]=\"col.stackDataLabel()\"\n [attr.data-column]=\"col.name()\"\n >\n @if (col.cellDef(); as cdef) {\n <ng-container\n [ngTemplateOutlet]=\"cdef.template\"\n [ngTemplateOutletContext]=\"col.buildCellContext(row, i, c, f, l, e, o)\"\n />\n }\n </td>\n @if (col.footerCellDef()) {\n <td\n cdk-footer-cell\n *cdkFooterCellDef\n [class]=\"col.footerCellClasses()\"\n [style.width]=\"col.resolvedWidth()\"\n [attr.data-column]=\"col.name()\"\n >\n <ng-container\n [ngTemplateOutlet]=\"col.footerCellDef()!.template\"\n [ngTemplateOutletContext]=\"col.footerContext()\"\n />\n </td>\n }\n </ng-container>\n }\n\n <!-- Selection pseudo-column emitted as a leading column when `selection.enabled` is true.\n Hosts the master \"select all\" checkbox in the header and per-row checkboxes in the body.\n The role=\"checkbox\" host on <tw-checkbox> is allowlisted by the row-click suppression\n helper so clicking it does NOT also emit `rowClicked`. -->\n @if (resolvedSelection().enabled) {\n <ng-container cdkColumnDef=\"_selection\">\n <th\n cdk-header-cell\n *cdkHeaderCellDef\n [class]=\"selectionHeaderClasses()\"\n scope=\"col\"\n >\n <!-- Select-all needs a full row snapshot, which only the array\n data-source path can produce. Rendering it for an Observable /\n DataSource input gave a checkbox that looked functional, never\n reflected state, and did nothing on click. -->\n @if (dataIsArray()) {\n <tw-checkbox\n size=\"sm\"\n [checked]=\"masterChecked()\"\n [indeterminate]=\"masterIndeterminate()\"\n (change)=\"toggleMasterSelection($event)\"\n [aria-label]=\"resolvedLabels().selectAllLabel\"\n />\n }\n </th>\n <td\n cdk-cell\n *cdkCellDef=\"let row; let i = index\"\n [class]=\"selectionCellClasses()\"\n >\n <tw-checkbox\n size=\"sm\"\n [checked]=\"isSelected(row)\"\n (change)=\"setSelected(row, $event)\"\n [aria-label]=\"selectRowLabel(i)\"\n />\n </td>\n </ng-container>\n }\n\n <!-- Auto-generated row defs driven by visibleColumnNames(). -->\n @if (visibleColumnNames().length > 0) {\n <tr\n cdk-header-row\n *cdkHeaderRowDef=\"visibleColumnNames(); sticky: stickyHeader()\"\n ></tr>\n <tr\n cdk-row\n *cdkRowDef=\"let row; let i = dataIndex; columns: visibleColumnNames()\"\n (click)=\"handleRowClick(row, i, $event)\"\n [attr.aria-selected]=\"rowAriaSelected(row)\"\n ></tr>\n @if (hasFooterRow()) {\n <tr\n cdk-footer-row\n *cdkFooterRowDef=\"visibleColumnNames(); sticky: stickyFooter()\"\n ></tr>\n }\n }\n\n <!-- Expansion pseudo-column + extra row def. -->\n @if (hasExpansion()) {\n <ng-container cdkColumnDef=\"_tw_expansion\">\n <td\n cdk-cell\n *cdkCellDef=\"let row; let i = dataIndex\"\n [attr.colspan]=\"expansionColspan()\"\n [class]=\"expansionCellClasses()\"\n >\n @if (expansionTemplate(); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl.template\"\n [ngTemplateOutletContext]=\"expansionContext(row, i)\"\n />\n }\n </td>\n </ng-container>\n <tr\n cdk-row\n *cdkRowDef=\"let row; columns: ['_tw_expansion']; when: expansionWhen\"\n [class]=\"expansionRowClasses()\"\n ></tr>\n }\n\n <!-- Consumer-projected content: tw-column metadata components and ng-template-based\n directives (*twNoDataRow, *twRowExpansion). The tw-column hosts are display:none\n so they are harmless inside the <table>. -->\n <ng-content />\n </table>\n\n <!-- Empty-state fallback overlay (hidden when a *twNoDataRow is projected; hidden when loading/error). -->\n @if (shouldRenderEmptyOverlay()) {\n <div [class]=\"emptyStateClasses()\">\n <ng-content select=\"[slot='empty']\">\n <svg\n [class]=\"emptyIconClasses()\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"M2.25 13.5h3.86a2.25 2.25 0 0 1 2.012 1.244l.256.512a2.25 2.25 0 0 0 2.013 1.244h3.218a2.25 2.25 0 0 0 2.013-1.244l.256-.512a2.25 2.25 0 0 1 2.013-1.244h3.859m-19.5.338V18a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18v-4.162c0-.224-.034-.447-.1-.661l-2.4-8.022A2.25 2.25 0 0 0 17.128 3.5H6.873a2.25 2.25 0 0 0-2.122 1.655l-2.4 8.022a2.25 2.25 0 0 0-.1.661Z\"\n />\n </svg>\n <p [class]=\"emptyMessageClasses()\">{{ resolvedLabels().empty }}</p>\n </ng-content>\n </div>\n }\n\n <!-- Error-state overlay. -->\n @if (shouldRenderErrorOverlay()) {\n <div [class]=\"errorStateClasses()\" role=\"alert\">\n <ng-content select=\"[slot='error']\">\n <svg\n [class]=\"errorIconClasses()\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z\"\n />\n </svg>\n <p [class]=\"errorMessageClasses()\">{{ errorMessage() }}</p>\n </ng-content>\n </div>\n }\n\n <!-- Loading overlay (absolute-positioned inside scroll wrapper). -->\n @if (loading()) {\n <div\n [class]=\"loadingOverlayClasses()\"\n role=\"status\"\n aria-live=\"polite\"\n animate.enter=\"fade-in\"\n animate.leave=\"fade-out\"\n >\n <ng-content select=\"[slot='loading']\">\n <svg\n class=\"size-5 shrink-0 animate-spin motion-reduce:animate-none text-fg-muted\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n aria-hidden=\"true\"\n >\n <circle\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n opacity=\"0.2\"\n />\n <circle\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n stroke-dasharray=\"60\"\n stroke-dashoffset=\"40\"\n />\n </svg>\n <span [class]=\"loadingMessageClasses()\">{{ resolvedLabels().loading }}</span>\n </ng-content>\n </div>\n }\n </div>\n</div>\n\n<!-- Footer slot \u2014 hidden when empty via `empty:hidden` in the slot class. -->\n<div [class]=\"footerSlotClasses()\">\n <ng-content select=\"[slot='footer']\" />\n</div>\n\n<!-- Pagination slot \u2014 hidden when empty via `empty:hidden` in the slot class. -->\n<div [class]=\"paginationSlotClasses()\">\n <ng-content select=\"[slot='pagination']\" />\n</div>\n" }]
1135
- }], ctorParameters: () => [], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], trackBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "trackBy", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", required: false }] }], sticky: [{ type: i0.Input, args: [{ isSignal: true, alias: "sticky", required: false }] }], responsive: [{ type: i0.Input, args: [{ isSignal: true, alias: "responsive", required: false }] }], selection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }], multiTemplateRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiTemplateRows", required: false }] }], labels: [{ type: i0.Input, args: [{ isSignal: true, alias: "labels", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], ariaLabelledby: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-labelledby", required: false }] }], expandedRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedRows", required: false }] }, { type: i0.Output, args: ["expandedRowsChange"] }], selected: [{ type: i0.Input, args: [{ isSignal: true, alias: "selected", required: false }] }, { type: i0.Output, args: ["selectedChange"] }], rowClicked: [{ type: i0.Output, args: ["rowClicked"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], expansionChange: [{ type: i0.Output, args: ["expansionChange"] }], columns: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => ColumnComponent), { ...{ descendants: true }, isSignal: true }] }], noDataRow: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NoDataRowDirective), { ...{ descendants: true }, isSignal: true }] }], expansionTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => RowExpansionDirective), { ...{ descendants: true }, isSignal: true }] }], cdkTable: [{ type: i0.ViewChild, args: ['cdkTable', { isSignal: true }] }] } });
1195
+ }, template: "<!-- Toolbar slot \u2014 hidden when empty via `empty:hidden` in the slot class. -->\n<div [class]=\"toolbarClasses()\">\n <ng-content select=\"[slot='toolbar']\" />\n</div>\n\n<!-- Scroll wrapper \u2014 also hosts the loading overlay as an absolutely-positioned child. -->\n<div [class]=\"scrollWrapperClasses()\">\n <div [class]=\"scrollContainerClasses()\" [style.max-height]=\"maxHeightStyle()\">\n <table\n #cdkTable=\"cdkTable\"\n cdk-table\n [class]=\"tableClasses()\"\n [attr.aria-label]=\"ariaLabel() ?? null\"\n [attr.aria-labelledby]=\"ariaLabelledby() ?? null\"\n >\n <ng-content select=\"[slot='caption']\" />\n\n <!--\n Emit one <ng-container cdkColumnDef> per visible column. CdkColumnDef's\n @ContentChildren(CdkCellDef / CdkHeaderCellDef / CdkFooterCellDef)\n resolves the <th *cdkHeaderCellDef> / <td *cdkCellDef> / <td *cdkFooterCellDef>\n below \u2014 those renderers delegate to the consumer's projected templates\n (col.cellDef() / col.headerCellDef() / col.footerCellDef()) via\n ngTemplateOutlet.\n -->\n @for (col of visibleColumns(); track col.name()) {\n <ng-container\n [cdkColumnDef]=\"col.name()\"\n [sticky]=\"col.stickyStart()\"\n [stickyEnd]=\"col.stickyEnd()\"\n >\n <th\n cdk-header-cell\n *cdkHeaderCellDef\n [class]=\"col.headerCellClasses()\"\n [style.width]=\"col.resolvedWidth()\"\n [attr.aria-sort]=\"col.ariaSort()\"\n [attr.data-column]=\"col.name()\"\n scope=\"col\"\n >\n @if (col.headerCellDef(); as hdef) {\n <ng-container\n [ngTemplateOutlet]=\"hdef.template\"\n [ngTemplateOutletContext]=\"col.headerContext()\"\n />\n } @else if (col.headerLabel()) {\n {{ col.headerLabel() }}\n }\n </th>\n <td\n cdk-cell\n *cdkCellDef=\"let row; let i = index; let c = count; let f = first; let l = last; let e = even; let o = odd\"\n [class]=\"col.cellClasses()\"\n [style.width]=\"col.resolvedWidth()\"\n [attr.data-label]=\"col.stackDataLabel()\"\n [attr.data-column]=\"col.name()\"\n >\n @if (col.cellDef(); as cdef) {\n <ng-container\n [ngTemplateOutlet]=\"cdef.template\"\n [ngTemplateOutletContext]=\"col.buildCellContext(row, i, c, f, l, e, o)\"\n />\n }\n </td>\n @if (col.footerCellDef()) {\n <td\n cdk-footer-cell\n *cdkFooterCellDef\n [class]=\"col.footerCellClasses()\"\n [style.width]=\"col.resolvedWidth()\"\n [attr.data-column]=\"col.name()\"\n >\n <ng-container\n [ngTemplateOutlet]=\"col.footerCellDef()!.template\"\n [ngTemplateOutletContext]=\"col.footerContext()\"\n />\n </td>\n }\n </ng-container>\n }\n\n <!-- Selection pseudo-column emitted as a leading column when `selection.enabled` is true.\n Hosts the master \"select all\" checkbox in the header and per-row checkboxes in the body.\n The role=\"checkbox\" host on <tw-checkbox> is allowlisted by the row-click suppression\n helper so clicking it does NOT also emit `rowClicked`. -->\n @if (resolvedSelection().enabled) {\n <ng-container cdkColumnDef=\"_selection\">\n <th\n cdk-header-cell\n *cdkHeaderCellDef\n [class]=\"selectionHeaderClasses()\"\n scope=\"col\"\n >\n <!-- Select-all needs a full row snapshot, which only the array\n data-source path can produce. Rendering it for an Observable /\n DataSource input gave a checkbox that looked functional, never\n reflected state, and did nothing on click. -->\n @if (dataIsArray()) {\n <tw-checkbox\n size=\"sm\"\n [checked]=\"masterChecked()\"\n [indeterminate]=\"masterIndeterminate()\"\n (change)=\"toggleMasterSelection($event)\"\n [aria-label]=\"resolvedLabels().selectAllLabel\"\n />\n } @else {\n <!-- Without the master checkbox this header cell would be\n literally empty, which axe reports as `empty-table-header`\n and which leaves the column unnamed in a screen reader's\n table summary. -->\n <span class=\"sr-only\">{{ resolvedLabels().selectionColumnLabel }}</span>\n }\n </th>\n <td\n cdk-cell\n *cdkCellDef=\"let row; let i = index\"\n [class]=\"selectionCellClasses()\"\n >\n <tw-checkbox\n size=\"sm\"\n [checked]=\"isSelected(row)\"\n (change)=\"setSelected(row, $event)\"\n [aria-label]=\"selectRowLabel(i)\"\n />\n </td>\n </ng-container>\n }\n\n <!-- Auto-generated row defs driven by visibleColumnNames(). -->\n @if (visibleColumnNames().length > 0) {\n <tr\n cdk-header-row\n *cdkHeaderRowDef=\"visibleColumnNames(); sticky: stickyHeader()\"\n ></tr>\n <!-- Both index context variables are captured on purpose. CDK's\n `CdkTable` sets `dataIndex` ONLY when `multiTemplateDataRows` is\n enabled (row expansion); otherwise it sets `index` and leaves\n `dataIndex` undefined. Reading just one gives `undefined` in the\n other mode \u2014 which shipped as `rowClicked` emitting\n `index: undefined` for every table without row expansion. -->\n <!-- `tabindex` / `(keydown)` / the focus ring are all gated on\n `clickableRows`. A static table must not put every row in the tab\n order, and Angular gives no way to ask whether `(rowClicked)` has a\n subscriber \u2014 so the consumer declares it. Deliberately NO `role`\n override: a `<tr>` is a `row`, and retagging it `button` breaks the\n table's structural role contract for its own cells. -->\n <tr\n cdk-row\n *cdkRowDef=\"\n let row;\n let i = index;\n let di = dataIndex;\n columns: visibleColumnNames()\n \"\n [class]=\"dataRowClasses()\"\n (click)=\"handleRowClick(row, di ?? i, $event)\"\n (keydown)=\"handleRowKeydown(row, di ?? i, $event)\"\n [attr.tabindex]=\"rowTabIndex()\"\n [attr.aria-selected]=\"rowAriaSelected(row)\"\n ></tr>\n @if (hasFooterRow()) {\n <tr\n cdk-footer-row\n *cdkFooterRowDef=\"visibleColumnNames(); sticky: stickyFooter()\"\n ></tr>\n }\n }\n\n <!-- Expansion pseudo-column + extra row def. -->\n @if (hasExpansion()) {\n <ng-container cdkColumnDef=\"_tw_expansion\">\n <td\n cdk-cell\n *cdkCellDef=\"let row; let i = dataIndex\"\n [attr.colspan]=\"expansionColspan()\"\n [class]=\"expansionCellClasses()\"\n >\n @if (expansionTemplate(); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl.template\"\n [ngTemplateOutletContext]=\"expansionContext(row, i)\"\n />\n }\n </td>\n </ng-container>\n <tr\n cdk-row\n *cdkRowDef=\"let row; columns: ['_tw_expansion']; when: expansionWhen\"\n [class]=\"expansionRowClasses()\"\n ></tr>\n }\n\n <!-- Consumer-projected content: tw-column metadata components and ng-template-based\n directives (*twNoDataRow, *twRowExpansion). The tw-column hosts are display:none\n so they are harmless inside the <table>. -->\n <ng-content />\n </table>\n\n <!-- Empty-state fallback overlay (hidden when a *twNoDataRow is projected; hidden when loading/error). -->\n @if (shouldRenderEmptyOverlay()) {\n <div [class]=\"emptyStateClasses()\">\n <ng-content select=\"[slot='empty']\">\n <svg\n [class]=\"emptyIconClasses()\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"M2.25 13.5h3.86a2.25 2.25 0 0 1 2.012 1.244l.256.512a2.25 2.25 0 0 0 2.013 1.244h3.218a2.25 2.25 0 0 0 2.013-1.244l.256-.512a2.25 2.25 0 0 1 2.013-1.244h3.859m-19.5.338V18a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18v-4.162c0-.224-.034-.447-.1-.661l-2.4-8.022A2.25 2.25 0 0 0 17.128 3.5H6.873a2.25 2.25 0 0 0-2.122 1.655l-2.4 8.022a2.25 2.25 0 0 0-.1.661Z\"\n />\n </svg>\n <p [class]=\"emptyMessageClasses()\">{{ resolvedLabels().empty }}</p>\n </ng-content>\n </div>\n }\n\n <!-- Error-state overlay. -->\n @if (shouldRenderErrorOverlay()) {\n <div [class]=\"errorStateClasses()\" role=\"alert\">\n <ng-content select=\"[slot='error']\">\n <svg\n [class]=\"errorIconClasses()\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z\"\n />\n </svg>\n <p [class]=\"errorMessageClasses()\">{{ errorMessage() }}</p>\n </ng-content>\n </div>\n }\n\n <!--\n Loading overlay (absolute-positioned inside scroll wrapper).\n\n Deliberately NOT a live region. The loading string is announced by\n `LiveAnnouncer` from the effect in table.ts; carrying `role=\"status\"`\n (which implies `aria-live=\"polite\"`) here as well made screen readers\n announce the same text twice on every fetch. The visible text stays \u2014\n it is the sighted user's loading affordance \u2014 it just no longer\n announces itself.\n -->\n @if (loading()) {\n <div\n [class]=\"loadingOverlayClasses()\"\n data-tw-table-loading\n animate.enter=\"fade-in\"\n animate.leave=\"fade-out\"\n >\n <ng-content select=\"[slot='loading']\">\n <svg\n class=\"size-5 shrink-0 animate-spin motion-reduce:animate-none text-fg-muted\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n aria-hidden=\"true\"\n >\n <circle\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n opacity=\"0.2\"\n />\n <circle\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n stroke-dasharray=\"60\"\n stroke-dashoffset=\"40\"\n />\n </svg>\n <span [class]=\"loadingMessageClasses()\">{{ resolvedLabels().loading }}</span>\n </ng-content>\n </div>\n }\n </div>\n</div>\n\n<!-- Footer slot \u2014 hidden when empty via `empty:hidden` in the slot class. -->\n<div [class]=\"footerSlotClasses()\">\n <ng-content select=\"[slot='footer']\" />\n</div>\n\n<!-- Pagination slot \u2014 hidden when empty via `empty:hidden` in the slot class. -->\n<div [class]=\"paginationSlotClasses()\">\n <ng-content select=\"[slot='pagination']\" />\n</div>\n" }]
1196
+ }], ctorParameters: () => [], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], trackBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "trackBy", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", required: false }] }], sticky: [{ type: i0.Input, args: [{ isSignal: true, alias: "sticky", required: false }] }], responsive: [{ type: i0.Input, args: [{ isSignal: true, alias: "responsive", required: false }] }], selection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }], multiTemplateRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiTemplateRows", required: false }] }], clickableRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "clickableRows", required: false }] }], labels: [{ type: i0.Input, args: [{ isSignal: true, alias: "labels", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], ariaLabelledby: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-labelledby", required: false }] }], expandedRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedRows", required: false }] }, { type: i0.Output, args: ["expandedRowsChange"] }], selected: [{ type: i0.Input, args: [{ isSignal: true, alias: "selected", required: false }] }, { type: i0.Output, args: ["selectedChange"] }], rowClicked: [{ type: i0.Output, args: ["rowClicked"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], expansionChange: [{ type: i0.Output, args: ["expansionChange"] }], columns: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => ColumnComponent), { ...{ descendants: true }, isSignal: true }] }], noDataRow: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NoDataRowDirective), { ...{ descendants: true }, isSignal: true }] }], expansionTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => RowExpansionDirective), { ...{ descendants: true }, isSignal: true }] }], cdkTable: [{ type: i0.ViewChild, args: ['cdkTable', { isSignal: true }] }] } });
1136
1197
 
1137
1198
  /**
1138
1199
  * Generated bundle index. Do not edit.