@frame-kit/ui-ng 0.2.4 → 0.2.6

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.
@@ -116,6 +116,7 @@ const DEFAULT_TABLE_CONFIG = {
116
116
  showHeader: true,
117
117
  rowExpansion: false,
118
118
  emptyMessage: 'No data available',
119
+ groupBy: null,
119
120
  trackBy: (index) => index,
120
121
  rowStyleFn: null,
121
122
  virtualization: 'auto',
@@ -184,6 +185,34 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
184
185
  type: Injectable
185
186
  }] });
186
187
 
188
+ /**
189
+ * The group heading to emit before a row, or null when the row continues the
190
+ * group above it.
191
+ *
192
+ * Shared by the semantic-table and stack render paths so the two cannot answer
193
+ * the question differently — a heading appearing on desktop and not on mobile
194
+ * is the kind of drift that only shows up on someone's phone.
195
+ *
196
+ * Reads the rows in the order it is given rather than reordering, so a caller
197
+ * sorting on one field while grouping by another sees the group open more than
198
+ * once. That is deliberate: silently merging them would report a grouping the
199
+ * data does not have.
200
+ */
201
+ function groupHeadingForRow(rows, row, index, groupBy) {
202
+ if (!groupBy) {
203
+ return null;
204
+ }
205
+ const key = groupBy(row);
206
+ if (key === null) {
207
+ return null;
208
+ }
209
+ if (index === 0) {
210
+ return key;
211
+ }
212
+ const previous = rows[index - 1];
213
+ return previous !== undefined && groupBy(previous) === key ? null : key;
214
+ }
215
+
187
216
  class DataTableStackComponent {
188
217
  /** Row data array rendered as stacked cards in mobile layout. */
189
218
  rows = input([], ...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
@@ -193,6 +222,12 @@ class DataTableStackComponent {
193
222
  config = input(null, ...(ngDevMode ? [{ debugName: "config" }] : /* istanbul ignore next */ []));
194
223
  /** TrackBy function used by the `@for` loop over rows. */
195
224
  trackByFn = input((index) => index, ...(ngDevMode ? [{ debugName: "trackByFn" }] : /* istanbul ignore next */ []));
225
+ // ===== GROUPING =====
226
+ /**
227
+ * Template rendered inside a group heading. Mirrors the semantic-table path
228
+ * so a grouped table looks grouped at every viewport.
229
+ */
230
+ groupHeadingRenderer = input(null, ...(ngDevMode ? [{ debugName: "groupHeadingRenderer" }] : /* istanbul ignore next */ []));
196
231
  // ===== SELECTION =====
197
232
  // Mirrors the host data-table's selection inputs. The stack path renders a
198
233
  // checkbox at the top-left of each card when `rowSelection` is true and
@@ -208,6 +243,10 @@ class DataTableStackComponent {
208
243
  selectionDisabled = input(false, ...(ngDevMode ? [{ debugName: "selectionDisabled" }] : /* istanbul ignore next */ []));
209
244
  /** Accessible label for each card's selection checkbox. */
210
245
  selectionLabel = input('Select row', ...(ngDevMode ? [{ debugName: "selectionLabel" }] : /* istanbul ignore next */ []));
246
+ /** The group heading to open before this card, if it starts a new group. */
247
+ groupHeadingFor(row, index) {
248
+ return groupHeadingForRow(this.rows(), row, index, this.config()?.groupBy ?? null);
249
+ }
211
250
  /** Fires when the user clicks a card row, emitting the row context. */
212
251
  rowClick = output();
213
252
  /** Fires when the user toggles a card's selection checkbox, bubbling up to the host. */
@@ -281,12 +320,12 @@ class DataTableStackComponent {
281
320
  return row;
282
321
  }
283
322
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataTableStackComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
284
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DataTableStackComponent, isStandalone: true, selector: "fk-data-table-stack", inputs: { rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, trackByFn: { classPropertyName: "trackByFn", publicName: "trackByFn", isSignal: true, isRequired: false, transformFunction: null }, rowSelection: { classPropertyName: "rowSelection", publicName: "rowSelection", isSignal: true, isRequired: false, transformFunction: null }, selectedKeys: { classPropertyName: "selectedKeys", publicName: "selectedKeys", isSignal: true, isRequired: false, transformFunction: null }, getRowId: { classPropertyName: "getRowId", publicName: "getRowId", isSignal: true, isRequired: false, transformFunction: null }, selectionDisabled: { classPropertyName: "selectionDisabled", publicName: "selectionDisabled", isSignal: true, isRequired: false, transformFunction: null }, selectionLabel: { classPropertyName: "selectionLabel", publicName: "selectionLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rowClick: "rowClick", toggleRow: "toggleRow" }, ngImport: i0, template: "<div class=\"fk-data-table-stack\">\n @for (row of rows(); track trackByFn()($index, row); let i = $index) {\n <div\n class=\"fk-data-table-stack__card\"\n [class.fk-data-table-stack__card--selected]=\"isRowSelected(row)\"\n >\n @if (rowSelection()) {\n <div class=\"fk-data-table-stack__select\">\n <input\n type=\"checkbox\"\n class=\"fk-data-table-stack__checkbox\"\n [attr.aria-label]=\"selectionLabel()\"\n [checked]=\"isRowSelected(row)\"\n [disabled]=\"isRowSelectionDisabled(row)\"\n (click)=\"onToggleSelection(row, $event)\"\n />\n </div>\n }\n\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div class=\"fk-data-table-stack__primary\" (click)=\"onRowClick(row, i)\">\n @for (column of primaryColumns(); track column.id) {\n <div class=\"fk-data-table-stack__field\">\n <span class=\"fk-data-table-stack__label\">{{\n getLabel(column)\n }}</span>\n <span class=\"fk-data-table-stack__value\">\n @if (column.cellRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.cellRenderer\"\n [ngTemplateOutletContext]=\"{\n $implicit: getCellValue(column, row),\n row: row,\n }\"\n />\n } @else {\n {{ getCellValue(column, row) }}\n }\n </span>\n </div>\n }\n </div>\n\n @if (hasSecondary()) {\n @if (isExpanded(i)) {\n <div class=\"fk-data-table-stack__secondary\">\n @for (column of secondaryColumns(); track column.id) {\n <div class=\"fk-data-table-stack__field\">\n <span class=\"fk-data-table-stack__label\">{{\n getLabel(column)\n }}</span>\n <span class=\"fk-data-table-stack__value\">\n @if (column.cellRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.cellRenderer\"\n [ngTemplateOutletContext]=\"{\n $implicit: getCellValue(column, row),\n row: row,\n }\"\n />\n } @else {\n {{ getCellValue(column, row) }}\n }\n </span>\n </div>\n }\n </div>\n }\n\n <button\n class=\"fk-data-table-stack__toggle\"\n type=\"button\"\n [attr.aria-expanded]=\"isExpanded(i)\"\n (click)=\"toggleExpand(i)\"\n >\n {{ isExpanded(i) ? 'Show less' : 'Show more' }}\n </button>\n }\n </div>\n }\n\n @if (rows().length === 0) {\n <div class=\"fk-data-table-stack__empty\">\n {{ config()?.emptyMessage ?? 'No data available' }}\n </div>\n }\n</div>\n", styles: [":host{display:block}.fk-data-table-stack{display:flex;flex-direction:column;gap:var(--fk-data-table-stack-gap, var(--fk-rhythm-3, .75rem))}.fk-data-table-stack__card{background:var(--fk-data-table-body-bg, var(--fk-color-surface, #ffffff));border:1px solid var(--fk-data-table-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-data-table-stack-radius, var(--fk-radius-md, .5rem));overflow:hidden;position:relative}.fk-data-table-stack__card--selected{background:var(--fk-data-table-row-selected-bg, var(--fk-color-primary-light, #eaf4ff))}.fk-data-table-stack__select{padding:var(--fk-rhythm-2, .5rem) var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem)) 0}.fk-data-table-stack__checkbox{appearance:none;width:var(--fk-data-table-checkbox-size, 1rem);height:var(--fk-data-table-checkbox-size, 1rem);margin:0;border:1px solid var(--fk-data-table-checkbox-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-data-table-checkbox-radius, .25rem);background:var(--fk-data-table-checkbox-bg, var(--fk-color-surface, #ffffff));cursor:pointer;position:relative;vertical-align:middle}.fk-data-table-stack__checkbox:focus-visible{outline:none;box-shadow:var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18))}.fk-data-table-stack__checkbox:checked{background:var(--fk-data-table-checkbox-checked-bg, var(--fk-color-primary, #0a84ff));border-color:var(--fk-data-table-checkbox-checked-border-color, var(--fk-color-primary, #0a84ff))}.fk-data-table-stack__checkbox:checked:after{content:\"\";position:absolute;inset:0;background:var(--fk-data-table-checkbox-icon-color, var(--fk-white, #ffffff));mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='currentColor' d='M6.5 11L3 7.5l1-1L6.5 9 12 3.5l1 1z'/></svg>\") center/75% 75% no-repeat;-webkit-mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='currentColor' d='M6.5 11L3 7.5l1-1L6.5 9 12 3.5l1 1z'/></svg>\") center/75% 75% no-repeat}.fk-data-table-stack__checkbox:disabled{cursor:not-allowed;opacity:.5}.fk-data-table-stack__primary{padding:var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem));cursor:pointer}.fk-data-table-stack__primary:hover{background:var(--fk-data-table-row-hover-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-data-table-stack__secondary{padding:0 var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem)) var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem))}.fk-data-table-stack__field{display:flex;justify-content:space-between;align-items:baseline;padding:var(--fk-rhythm-1, .25rem) 0}.fk-data-table-stack__field:not(:last-child){border-bottom:1px solid var(--fk-data-table-stack-field-border, var(--fk-color-border-light, rgba(0, 0, 0, .06)))}.fk-data-table-stack__label{font-size:var(--fk-data-table-head-font-size, var(--fk-typography-small-font-size, .875rem));font-weight:var(--fk-data-table-head-font-weight, var(--fk-font-weight-semibold, 600));color:var(--fk-data-table-head-color, var(--fk-color-text-muted, #6b7a8d));flex-shrink:0;margin-inline-end:var(--fk-rhythm-3, .75rem)}.fk-data-table-stack__value{font-size:var(--fk-data-table-body-font-size, var(--fk-typography-body-font-size, .875rem));color:var(--fk-data-table-body-color, var(--fk-color-text, #1f2d3d));text-align:end;word-break:break-word}.fk-data-table-stack__toggle{display:block;width:100%;padding:var(--fk-rhythm-2, .5rem);border:none;border-top:1px solid var(--fk-data-table-border-color, var(--fk-color-border, #d9e2ee));background:var(--fk-data-table-head-bg, var(--fk-color-surface-muted, #f7f9fb));color:var(--fk-data-table-stack-toggle-color, var(--fk-color-primary, #3b82f6));font-size:var(--fk-typography-small-font-size, .875rem);font-weight:var(--fk-font-weight-semibold, 600);cursor:pointer;text-align:center}.fk-data-table-stack__toggle:hover{background:var(--fk-data-table-row-hover-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-data-table-stack__toggle:focus-visible{outline:2px solid var(--fk-color-primary, #3b82f6);outline-offset:-2px}.fk-data-table-stack__empty{padding:var(--fk-data-table-empty-padding, var(--fk-rhythm-8, 2rem));text-align:center;color:var(--fk-data-table-empty-color, var(--fk-color-text-muted, #6b7a8d));font-size:var(--fk-data-table-body-font-size, var(--fk-typography-body-font-size, .875rem))}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
323
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DataTableStackComponent, isStandalone: true, selector: "fk-data-table-stack", inputs: { rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, trackByFn: { classPropertyName: "trackByFn", publicName: "trackByFn", isSignal: true, isRequired: false, transformFunction: null }, groupHeadingRenderer: { classPropertyName: "groupHeadingRenderer", publicName: "groupHeadingRenderer", isSignal: true, isRequired: false, transformFunction: null }, rowSelection: { classPropertyName: "rowSelection", publicName: "rowSelection", isSignal: true, isRequired: false, transformFunction: null }, selectedKeys: { classPropertyName: "selectedKeys", publicName: "selectedKeys", isSignal: true, isRequired: false, transformFunction: null }, getRowId: { classPropertyName: "getRowId", publicName: "getRowId", isSignal: true, isRequired: false, transformFunction: null }, selectionDisabled: { classPropertyName: "selectionDisabled", publicName: "selectionDisabled", isSignal: true, isRequired: false, transformFunction: null }, selectionLabel: { classPropertyName: "selectionLabel", publicName: "selectionLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rowClick: "rowClick", toggleRow: "toggleRow" }, ngImport: i0, template: "<div class=\"fk-data-table-stack\">\n @for (row of rows(); track trackByFn()($index, row); let i = $index) {\n @if (groupHeadingFor(row, i); as heading) {\n <div class=\"fk-data-table-stack__group-heading\">\n @if (groupHeadingRenderer()) {\n <ng-container\n [ngTemplateOutlet]=\"groupHeadingRenderer()!\"\n [ngTemplateOutletContext]=\"{ $implicit: heading, row: row }\"\n />\n } @else {\n {{ heading }}\n }\n </div>\n }\n\n <div\n class=\"fk-data-table-stack__card\"\n [class.fk-data-table-stack__card--selected]=\"isRowSelected(row)\"\n >\n @if (rowSelection()) {\n <div class=\"fk-data-table-stack__select\">\n <input\n type=\"checkbox\"\n class=\"fk-data-table-stack__checkbox\"\n [attr.aria-label]=\"selectionLabel()\"\n [checked]=\"isRowSelected(row)\"\n [disabled]=\"isRowSelectionDisabled(row)\"\n (click)=\"onToggleSelection(row, $event)\"\n />\n </div>\n }\n\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div class=\"fk-data-table-stack__primary\" (click)=\"onRowClick(row, i)\">\n @for (column of primaryColumns(); track column.id) {\n <div class=\"fk-data-table-stack__field\">\n <span class=\"fk-data-table-stack__label\">{{\n getLabel(column)\n }}</span>\n <span class=\"fk-data-table-stack__value\">\n @if (column.cellRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.cellRenderer\"\n [ngTemplateOutletContext]=\"{\n $implicit: getCellValue(column, row),\n row: row,\n }\"\n />\n } @else {\n {{ getCellValue(column, row) }}\n }\n </span>\n </div>\n }\n </div>\n\n @if (hasSecondary()) {\n @if (isExpanded(i)) {\n <div class=\"fk-data-table-stack__secondary\">\n @for (column of secondaryColumns(); track column.id) {\n <div class=\"fk-data-table-stack__field\">\n <span class=\"fk-data-table-stack__label\">{{\n getLabel(column)\n }}</span>\n <span class=\"fk-data-table-stack__value\">\n @if (column.cellRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.cellRenderer\"\n [ngTemplateOutletContext]=\"{\n $implicit: getCellValue(column, row),\n row: row,\n }\"\n />\n } @else {\n {{ getCellValue(column, row) }}\n }\n </span>\n </div>\n }\n </div>\n }\n\n <button\n class=\"fk-data-table-stack__toggle\"\n type=\"button\"\n [attr.aria-expanded]=\"isExpanded(i)\"\n (click)=\"toggleExpand(i)\"\n >\n {{ isExpanded(i) ? 'Show less' : 'Show more' }}\n </button>\n }\n </div>\n }\n\n @if (rows().length === 0) {\n <div class=\"fk-data-table-stack__empty\">\n {{ config()?.emptyMessage ?? 'No data available' }}\n </div>\n }\n</div>\n", styles: [":host{display:block}.fk-data-table-stack{display:flex;flex-direction:column;gap:var(--fk-data-table-stack-gap, var(--fk-rhythm-3, .75rem))}.fk-data-table-stack__card{background:var(--fk-data-table-body-bg, var(--fk-color-surface, #ffffff));border:1px solid var(--fk-data-table-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-data-table-stack-radius, var(--fk-radius-md, .5rem));overflow:hidden;position:relative}.fk-data-table-stack__card--selected{background:var(--fk-data-table-row-selected-bg, var(--fk-color-primary-light, #eaf4ff))}.fk-data-table-stack__select{padding:var(--fk-rhythm-2, .5rem) var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem)) 0}.fk-data-table-stack__checkbox{appearance:none;width:var(--fk-data-table-checkbox-size, 1rem);height:var(--fk-data-table-checkbox-size, 1rem);margin:0;border:1px solid var(--fk-data-table-checkbox-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-data-table-checkbox-radius, .25rem);background:var(--fk-data-table-checkbox-bg, var(--fk-color-surface, #ffffff));cursor:pointer;position:relative;vertical-align:middle}.fk-data-table-stack__checkbox:focus-visible{outline:none;box-shadow:var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18))}.fk-data-table-stack__checkbox:checked{background:var(--fk-data-table-checkbox-checked-bg, var(--fk-color-primary, #0a84ff));border-color:var(--fk-data-table-checkbox-checked-border-color, var(--fk-color-primary, #0a84ff))}.fk-data-table-stack__checkbox:checked:after{content:\"\";position:absolute;inset:0;background:var(--fk-data-table-checkbox-icon-color, var(--fk-white, #ffffff));mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='currentColor' d='M6.5 11L3 7.5l1-1L6.5 9 12 3.5l1 1z'/></svg>\") center/75% 75% no-repeat;-webkit-mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='currentColor' d='M6.5 11L3 7.5l1-1L6.5 9 12 3.5l1 1z'/></svg>\") center/75% 75% no-repeat}.fk-data-table-stack__checkbox:disabled{cursor:not-allowed;opacity:.5}.fk-data-table-stack__primary{padding:var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem));cursor:pointer}.fk-data-table-stack__primary:hover{background:var(--fk-data-table-row-hover-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-data-table-stack__secondary{padding:0 var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem)) var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem))}.fk-data-table-stack__field{display:flex;justify-content:space-between;align-items:baseline;padding:var(--fk-rhythm-1, .25rem) 0}.fk-data-table-stack__field:not(:last-child){border-bottom:1px solid var(--fk-data-table-stack-field-border, var(--fk-color-border-light, rgba(0, 0, 0, .06)))}.fk-data-table-stack__label{font-size:var(--fk-data-table-head-font-size, var(--fk-typography-small-font-size, .875rem));font-weight:var(--fk-data-table-head-font-weight, var(--fk-font-weight-semibold, 600));color:var(--fk-data-table-head-color, var(--fk-color-text-muted, #6b7a8d));flex-shrink:0;margin-inline-end:var(--fk-rhythm-3, .75rem)}.fk-data-table-stack__value{font-size:var(--fk-data-table-body-font-size, var(--fk-typography-body-font-size, .875rem));color:var(--fk-data-table-body-color, var(--fk-color-text, #1f2d3d));text-align:end;word-break:break-word}.fk-data-table-stack__toggle{display:block;width:100%;padding:var(--fk-rhythm-2, .5rem);border:none;border-top:1px solid var(--fk-data-table-border-color, var(--fk-color-border, #d9e2ee));background:var(--fk-data-table-head-bg, var(--fk-color-surface-muted, #f7f9fb));color:var(--fk-data-table-stack-toggle-color, var(--fk-color-primary, #3b82f6));font-size:var(--fk-typography-small-font-size, .875rem);font-weight:var(--fk-font-weight-semibold, 600);cursor:pointer;text-align:center}.fk-data-table-stack__toggle:hover{background:var(--fk-data-table-row-hover-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-data-table-stack__toggle:focus-visible{outline:2px solid var(--fk-color-primary, #3b82f6);outline-offset:-2px}.fk-data-table-stack__empty{padding:var(--fk-data-table-empty-padding, var(--fk-rhythm-8, 2rem));text-align:center;color:var(--fk-data-table-empty-color, var(--fk-color-text-muted, #6b7a8d));font-size:var(--fk-data-table-body-font-size, var(--fk-typography-body-font-size, .875rem))}.fk-data-table-stack__group-heading{padding:var(--fk-data-table-group-heading-padding, var(--fk-rhythm-2, .5rem) var(--fk-rhythm-3, .75rem));background:var(--fk-data-table-group-heading-bg, var(--fk-color-surface-muted, #f7f9fb));border-radius:var(--fk-data-table-stack-radius, var(--fk-radius-md, .5rem));font-size:var(--fk-data-table-group-heading-font-size, var(--fk-typography-caption-font-size, .75rem));font-weight:var(--fk-data-table-group-heading-font-weight, var(--fk-font-weight-semibold, 600));letter-spacing:var(--fk-data-table-group-heading-letter-spacing, .04em);text-transform:var(--fk-data-table-group-heading-text-transform, uppercase);color:var(--fk-data-table-group-heading-color, var(--fk-color-text-muted, #6b7a8d))}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
285
324
  }
286
325
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataTableStackComponent, decorators: [{
287
326
  type: Component,
288
- args: [{ selector: 'fk-data-table-stack', standalone: true, imports: [NgTemplateOutlet], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fk-data-table-stack\">\n @for (row of rows(); track trackByFn()($index, row); let i = $index) {\n <div\n class=\"fk-data-table-stack__card\"\n [class.fk-data-table-stack__card--selected]=\"isRowSelected(row)\"\n >\n @if (rowSelection()) {\n <div class=\"fk-data-table-stack__select\">\n <input\n type=\"checkbox\"\n class=\"fk-data-table-stack__checkbox\"\n [attr.aria-label]=\"selectionLabel()\"\n [checked]=\"isRowSelected(row)\"\n [disabled]=\"isRowSelectionDisabled(row)\"\n (click)=\"onToggleSelection(row, $event)\"\n />\n </div>\n }\n\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div class=\"fk-data-table-stack__primary\" (click)=\"onRowClick(row, i)\">\n @for (column of primaryColumns(); track column.id) {\n <div class=\"fk-data-table-stack__field\">\n <span class=\"fk-data-table-stack__label\">{{\n getLabel(column)\n }}</span>\n <span class=\"fk-data-table-stack__value\">\n @if (column.cellRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.cellRenderer\"\n [ngTemplateOutletContext]=\"{\n $implicit: getCellValue(column, row),\n row: row,\n }\"\n />\n } @else {\n {{ getCellValue(column, row) }}\n }\n </span>\n </div>\n }\n </div>\n\n @if (hasSecondary()) {\n @if (isExpanded(i)) {\n <div class=\"fk-data-table-stack__secondary\">\n @for (column of secondaryColumns(); track column.id) {\n <div class=\"fk-data-table-stack__field\">\n <span class=\"fk-data-table-stack__label\">{{\n getLabel(column)\n }}</span>\n <span class=\"fk-data-table-stack__value\">\n @if (column.cellRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.cellRenderer\"\n [ngTemplateOutletContext]=\"{\n $implicit: getCellValue(column, row),\n row: row,\n }\"\n />\n } @else {\n {{ getCellValue(column, row) }}\n }\n </span>\n </div>\n }\n </div>\n }\n\n <button\n class=\"fk-data-table-stack__toggle\"\n type=\"button\"\n [attr.aria-expanded]=\"isExpanded(i)\"\n (click)=\"toggleExpand(i)\"\n >\n {{ isExpanded(i) ? 'Show less' : 'Show more' }}\n </button>\n }\n </div>\n }\n\n @if (rows().length === 0) {\n <div class=\"fk-data-table-stack__empty\">\n {{ config()?.emptyMessage ?? 'No data available' }}\n </div>\n }\n</div>\n", styles: [":host{display:block}.fk-data-table-stack{display:flex;flex-direction:column;gap:var(--fk-data-table-stack-gap, var(--fk-rhythm-3, .75rem))}.fk-data-table-stack__card{background:var(--fk-data-table-body-bg, var(--fk-color-surface, #ffffff));border:1px solid var(--fk-data-table-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-data-table-stack-radius, var(--fk-radius-md, .5rem));overflow:hidden;position:relative}.fk-data-table-stack__card--selected{background:var(--fk-data-table-row-selected-bg, var(--fk-color-primary-light, #eaf4ff))}.fk-data-table-stack__select{padding:var(--fk-rhythm-2, .5rem) var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem)) 0}.fk-data-table-stack__checkbox{appearance:none;width:var(--fk-data-table-checkbox-size, 1rem);height:var(--fk-data-table-checkbox-size, 1rem);margin:0;border:1px solid var(--fk-data-table-checkbox-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-data-table-checkbox-radius, .25rem);background:var(--fk-data-table-checkbox-bg, var(--fk-color-surface, #ffffff));cursor:pointer;position:relative;vertical-align:middle}.fk-data-table-stack__checkbox:focus-visible{outline:none;box-shadow:var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18))}.fk-data-table-stack__checkbox:checked{background:var(--fk-data-table-checkbox-checked-bg, var(--fk-color-primary, #0a84ff));border-color:var(--fk-data-table-checkbox-checked-border-color, var(--fk-color-primary, #0a84ff))}.fk-data-table-stack__checkbox:checked:after{content:\"\";position:absolute;inset:0;background:var(--fk-data-table-checkbox-icon-color, var(--fk-white, #ffffff));mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='currentColor' d='M6.5 11L3 7.5l1-1L6.5 9 12 3.5l1 1z'/></svg>\") center/75% 75% no-repeat;-webkit-mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='currentColor' d='M6.5 11L3 7.5l1-1L6.5 9 12 3.5l1 1z'/></svg>\") center/75% 75% no-repeat}.fk-data-table-stack__checkbox:disabled{cursor:not-allowed;opacity:.5}.fk-data-table-stack__primary{padding:var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem));cursor:pointer}.fk-data-table-stack__primary:hover{background:var(--fk-data-table-row-hover-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-data-table-stack__secondary{padding:0 var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem)) var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem))}.fk-data-table-stack__field{display:flex;justify-content:space-between;align-items:baseline;padding:var(--fk-rhythm-1, .25rem) 0}.fk-data-table-stack__field:not(:last-child){border-bottom:1px solid var(--fk-data-table-stack-field-border, var(--fk-color-border-light, rgba(0, 0, 0, .06)))}.fk-data-table-stack__label{font-size:var(--fk-data-table-head-font-size, var(--fk-typography-small-font-size, .875rem));font-weight:var(--fk-data-table-head-font-weight, var(--fk-font-weight-semibold, 600));color:var(--fk-data-table-head-color, var(--fk-color-text-muted, #6b7a8d));flex-shrink:0;margin-inline-end:var(--fk-rhythm-3, .75rem)}.fk-data-table-stack__value{font-size:var(--fk-data-table-body-font-size, var(--fk-typography-body-font-size, .875rem));color:var(--fk-data-table-body-color, var(--fk-color-text, #1f2d3d));text-align:end;word-break:break-word}.fk-data-table-stack__toggle{display:block;width:100%;padding:var(--fk-rhythm-2, .5rem);border:none;border-top:1px solid var(--fk-data-table-border-color, var(--fk-color-border, #d9e2ee));background:var(--fk-data-table-head-bg, var(--fk-color-surface-muted, #f7f9fb));color:var(--fk-data-table-stack-toggle-color, var(--fk-color-primary, #3b82f6));font-size:var(--fk-typography-small-font-size, .875rem);font-weight:var(--fk-font-weight-semibold, 600);cursor:pointer;text-align:center}.fk-data-table-stack__toggle:hover{background:var(--fk-data-table-row-hover-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-data-table-stack__toggle:focus-visible{outline:2px solid var(--fk-color-primary, #3b82f6);outline-offset:-2px}.fk-data-table-stack__empty{padding:var(--fk-data-table-empty-padding, var(--fk-rhythm-8, 2rem));text-align:center;color:var(--fk-data-table-empty-color, var(--fk-color-text-muted, #6b7a8d));font-size:var(--fk-data-table-body-font-size, var(--fk-typography-body-font-size, .875rem))}\n"] }]
289
- }], propDecorators: { rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], trackByFn: [{ type: i0.Input, args: [{ isSignal: true, alias: "trackByFn", required: false }] }], rowSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowSelection", required: false }] }], selectedKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedKeys", required: false }] }], getRowId: [{ type: i0.Input, args: [{ isSignal: true, alias: "getRowId", required: false }] }], selectionDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionDisabled", required: false }] }], selectionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionLabel", required: false }] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }], toggleRow: [{ type: i0.Output, args: ["toggleRow"] }] } });
327
+ args: [{ selector: 'fk-data-table-stack', standalone: true, imports: [NgTemplateOutlet], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fk-data-table-stack\">\n @for (row of rows(); track trackByFn()($index, row); let i = $index) {\n @if (groupHeadingFor(row, i); as heading) {\n <div class=\"fk-data-table-stack__group-heading\">\n @if (groupHeadingRenderer()) {\n <ng-container\n [ngTemplateOutlet]=\"groupHeadingRenderer()!\"\n [ngTemplateOutletContext]=\"{ $implicit: heading, row: row }\"\n />\n } @else {\n {{ heading }}\n }\n </div>\n }\n\n <div\n class=\"fk-data-table-stack__card\"\n [class.fk-data-table-stack__card--selected]=\"isRowSelected(row)\"\n >\n @if (rowSelection()) {\n <div class=\"fk-data-table-stack__select\">\n <input\n type=\"checkbox\"\n class=\"fk-data-table-stack__checkbox\"\n [attr.aria-label]=\"selectionLabel()\"\n [checked]=\"isRowSelected(row)\"\n [disabled]=\"isRowSelectionDisabled(row)\"\n (click)=\"onToggleSelection(row, $event)\"\n />\n </div>\n }\n\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div class=\"fk-data-table-stack__primary\" (click)=\"onRowClick(row, i)\">\n @for (column of primaryColumns(); track column.id) {\n <div class=\"fk-data-table-stack__field\">\n <span class=\"fk-data-table-stack__label\">{{\n getLabel(column)\n }}</span>\n <span class=\"fk-data-table-stack__value\">\n @if (column.cellRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.cellRenderer\"\n [ngTemplateOutletContext]=\"{\n $implicit: getCellValue(column, row),\n row: row,\n }\"\n />\n } @else {\n {{ getCellValue(column, row) }}\n }\n </span>\n </div>\n }\n </div>\n\n @if (hasSecondary()) {\n @if (isExpanded(i)) {\n <div class=\"fk-data-table-stack__secondary\">\n @for (column of secondaryColumns(); track column.id) {\n <div class=\"fk-data-table-stack__field\">\n <span class=\"fk-data-table-stack__label\">{{\n getLabel(column)\n }}</span>\n <span class=\"fk-data-table-stack__value\">\n @if (column.cellRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.cellRenderer\"\n [ngTemplateOutletContext]=\"{\n $implicit: getCellValue(column, row),\n row: row,\n }\"\n />\n } @else {\n {{ getCellValue(column, row) }}\n }\n </span>\n </div>\n }\n </div>\n }\n\n <button\n class=\"fk-data-table-stack__toggle\"\n type=\"button\"\n [attr.aria-expanded]=\"isExpanded(i)\"\n (click)=\"toggleExpand(i)\"\n >\n {{ isExpanded(i) ? 'Show less' : 'Show more' }}\n </button>\n }\n </div>\n }\n\n @if (rows().length === 0) {\n <div class=\"fk-data-table-stack__empty\">\n {{ config()?.emptyMessage ?? 'No data available' }}\n </div>\n }\n</div>\n", styles: [":host{display:block}.fk-data-table-stack{display:flex;flex-direction:column;gap:var(--fk-data-table-stack-gap, var(--fk-rhythm-3, .75rem))}.fk-data-table-stack__card{background:var(--fk-data-table-body-bg, var(--fk-color-surface, #ffffff));border:1px solid var(--fk-data-table-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-data-table-stack-radius, var(--fk-radius-md, .5rem));overflow:hidden;position:relative}.fk-data-table-stack__card--selected{background:var(--fk-data-table-row-selected-bg, var(--fk-color-primary-light, #eaf4ff))}.fk-data-table-stack__select{padding:var(--fk-rhythm-2, .5rem) var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem)) 0}.fk-data-table-stack__checkbox{appearance:none;width:var(--fk-data-table-checkbox-size, 1rem);height:var(--fk-data-table-checkbox-size, 1rem);margin:0;border:1px solid var(--fk-data-table-checkbox-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-data-table-checkbox-radius, .25rem);background:var(--fk-data-table-checkbox-bg, var(--fk-color-surface, #ffffff));cursor:pointer;position:relative;vertical-align:middle}.fk-data-table-stack__checkbox:focus-visible{outline:none;box-shadow:var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18))}.fk-data-table-stack__checkbox:checked{background:var(--fk-data-table-checkbox-checked-bg, var(--fk-color-primary, #0a84ff));border-color:var(--fk-data-table-checkbox-checked-border-color, var(--fk-color-primary, #0a84ff))}.fk-data-table-stack__checkbox:checked:after{content:\"\";position:absolute;inset:0;background:var(--fk-data-table-checkbox-icon-color, var(--fk-white, #ffffff));mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='currentColor' d='M6.5 11L3 7.5l1-1L6.5 9 12 3.5l1 1z'/></svg>\") center/75% 75% no-repeat;-webkit-mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='currentColor' d='M6.5 11L3 7.5l1-1L6.5 9 12 3.5l1 1z'/></svg>\") center/75% 75% no-repeat}.fk-data-table-stack__checkbox:disabled{cursor:not-allowed;opacity:.5}.fk-data-table-stack__primary{padding:var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem));cursor:pointer}.fk-data-table-stack__primary:hover{background:var(--fk-data-table-row-hover-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-data-table-stack__secondary{padding:0 var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem)) var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem))}.fk-data-table-stack__field{display:flex;justify-content:space-between;align-items:baseline;padding:var(--fk-rhythm-1, .25rem) 0}.fk-data-table-stack__field:not(:last-child){border-bottom:1px solid var(--fk-data-table-stack-field-border, var(--fk-color-border-light, rgba(0, 0, 0, .06)))}.fk-data-table-stack__label{font-size:var(--fk-data-table-head-font-size, var(--fk-typography-small-font-size, .875rem));font-weight:var(--fk-data-table-head-font-weight, var(--fk-font-weight-semibold, 600));color:var(--fk-data-table-head-color, var(--fk-color-text-muted, #6b7a8d));flex-shrink:0;margin-inline-end:var(--fk-rhythm-3, .75rem)}.fk-data-table-stack__value{font-size:var(--fk-data-table-body-font-size, var(--fk-typography-body-font-size, .875rem));color:var(--fk-data-table-body-color, var(--fk-color-text, #1f2d3d));text-align:end;word-break:break-word}.fk-data-table-stack__toggle{display:block;width:100%;padding:var(--fk-rhythm-2, .5rem);border:none;border-top:1px solid var(--fk-data-table-border-color, var(--fk-color-border, #d9e2ee));background:var(--fk-data-table-head-bg, var(--fk-color-surface-muted, #f7f9fb));color:var(--fk-data-table-stack-toggle-color, var(--fk-color-primary, #3b82f6));font-size:var(--fk-typography-small-font-size, .875rem);font-weight:var(--fk-font-weight-semibold, 600);cursor:pointer;text-align:center}.fk-data-table-stack__toggle:hover{background:var(--fk-data-table-row-hover-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-data-table-stack__toggle:focus-visible{outline:2px solid var(--fk-color-primary, #3b82f6);outline-offset:-2px}.fk-data-table-stack__empty{padding:var(--fk-data-table-empty-padding, var(--fk-rhythm-8, 2rem));text-align:center;color:var(--fk-data-table-empty-color, var(--fk-color-text-muted, #6b7a8d));font-size:var(--fk-data-table-body-font-size, var(--fk-typography-body-font-size, .875rem))}.fk-data-table-stack__group-heading{padding:var(--fk-data-table-group-heading-padding, var(--fk-rhythm-2, .5rem) var(--fk-rhythm-3, .75rem));background:var(--fk-data-table-group-heading-bg, var(--fk-color-surface-muted, #f7f9fb));border-radius:var(--fk-data-table-stack-radius, var(--fk-radius-md, .5rem));font-size:var(--fk-data-table-group-heading-font-size, var(--fk-typography-caption-font-size, .75rem));font-weight:var(--fk-data-table-group-heading-font-weight, var(--fk-font-weight-semibold, 600));letter-spacing:var(--fk-data-table-group-heading-letter-spacing, .04em);text-transform:var(--fk-data-table-group-heading-text-transform, uppercase);color:var(--fk-data-table-group-heading-color, var(--fk-color-text-muted, #6b7a8d))}\n"] }]
328
+ }], propDecorators: { rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], trackByFn: [{ type: i0.Input, args: [{ isSignal: true, alias: "trackByFn", required: false }] }], groupHeadingRenderer: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupHeadingRenderer", required: false }] }], rowSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowSelection", required: false }] }], selectedKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedKeys", required: false }] }], getRowId: [{ type: i0.Input, args: [{ isSignal: true, alias: "getRowId", required: false }] }], selectionDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionDisabled", required: false }] }], selectionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionLabel", required: false }] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }], toggleRow: [{ type: i0.Output, args: ["toggleRow"] }] } });
290
329
 
291
330
  class ResponsiveStrategyService {
292
331
  // ===== INJECTIONS =====
@@ -748,6 +787,13 @@ class DataTableComponent {
748
787
  expandableRow = input(null, ...(ngDevMode ? [{ debugName: "expandableRow" }] : /* istanbul ignore next */ []));
749
788
  /** Accessible label for each row's expansion chevron. */
750
789
  expansionLabel = input('Toggle row details', ...(ngDevMode ? [{ debugName: "expansionLabel" }] : /* istanbul ignore next */ []));
790
+ // ===== GROUPING =====
791
+ /**
792
+ * Template rendered inside a group heading. Receives the group key and the
793
+ * first row of the group, so a heading can show more than the key it was
794
+ * grouped by. Omit it and the key is rendered as text.
795
+ */
796
+ groupHeadingRenderer = input(null, ...(ngDevMode ? [{ debugName: "groupHeadingRenderer" }] : /* istanbul ignore next */ []));
751
797
  // ===== OUTPUTS =====
752
798
  /** Fires when the user clicks a sortable column header, emitting the new sort state. */
753
799
  sortChange = output();
@@ -783,6 +829,13 @@ class DataTableComponent {
783
829
  useVirtualization = computed(() => {
784
830
  const config = this.mergedConfig();
785
831
  const rowCount = this.rows().length;
832
+ // Virtualization positions rows by multiplying an index by `rowHeight`.
833
+ // A group heading is not that tall, so every row after the first heading
834
+ // would be placed wrong. Grouping wins; the alternative is a table that
835
+ // silently misplaces its own rows.
836
+ if (config.groupBy) {
837
+ return false;
838
+ }
786
839
  if (config.virtualization === true) {
787
840
  return true;
788
841
  }
@@ -799,6 +852,20 @@ class DataTableComponent {
799
852
  virtualStartIndex = computed(() => {
800
853
  return this.virtEngine.visibleRange().start;
801
854
  }, ...(ngDevMode ? [{ debugName: "virtualStartIndex" }] : /* istanbul ignore next */ []));
855
+ /**
856
+ * How many cells a heading has to span — the visible columns plus whichever
857
+ * of the selection and expansion cells the table is currently rendering.
858
+ */
859
+ groupHeadingSpan = computed(() => {
860
+ let span = this.visibleColumns().length;
861
+ if (this.rowSelection()) {
862
+ span += 1;
863
+ }
864
+ if (this.hasExpansion()) {
865
+ span += 1;
866
+ }
867
+ return span;
868
+ }, ...(ngDevMode ? [{ debugName: "groupHeadingSpan" }] : /* istanbul ignore next */ []));
802
869
  isEmpty = computed(() => {
803
870
  return this.engine.processedRows().length === 0 && !this.loading();
804
871
  }, ...(ngDevMode ? [{ debugName: "isEmpty" }] : /* istanbul ignore next */ []));
@@ -894,6 +961,10 @@ class DataTableComponent {
894
961
  });
895
962
  }
896
963
  // ===== TEMPLATE HELPERS =====
964
+ /** The group heading to open before this row, if it starts a new group. */
965
+ groupHeadingFor(row, index) {
966
+ return groupHeadingForRow(this.engine.processedRows(), row, index, this.mergedConfig().groupBy);
967
+ }
897
968
  /** Resolves the display value for a cell given its column definition and row data. */
898
969
  getCellValue(column, row) {
899
970
  if (typeof column.accessor === 'function') {
@@ -1211,11 +1282,11 @@ class DataTableComponent {
1211
1282
  return sort.direction;
1212
1283
  }
1213
1284
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1214
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DataTableComponent, isStandalone: true, selector: "fk-data-table", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, sortState: { classPropertyName: "sortState", publicName: "sortState", isSignal: true, isRequired: false, transformFunction: null }, filterState: { classPropertyName: "filterState", publicName: "filterState", isSignal: true, isRequired: false, transformFunction: null }, pageState: { classPropertyName: "pageState", publicName: "pageState", isSignal: true, isRequired: false, transformFunction: null }, rowSelection: { classPropertyName: "rowSelection", publicName: "rowSelection", isSignal: true, isRequired: false, transformFunction: null }, selectedKeys: { classPropertyName: "selectedKeys", publicName: "selectedKeys", isSignal: true, isRequired: false, transformFunction: null }, getRowId: { classPropertyName: "getRowId", publicName: "getRowId", isSignal: true, isRequired: false, transformFunction: null }, selectionDisabled: { classPropertyName: "selectionDisabled", publicName: "selectionDisabled", isSignal: true, isRequired: false, transformFunction: null }, selectionColumnPosition: { classPropertyName: "selectionColumnPosition", publicName: "selectionColumnPosition", isSignal: true, isRequired: false, transformFunction: null }, selectionLabel: { classPropertyName: "selectionLabel", publicName: "selectionLabel", isSignal: true, isRequired: false, transformFunction: null }, rowDetailTemplate: { classPropertyName: "rowDetailTemplate", publicName: "rowDetailTemplate", isSignal: true, isRequired: false, transformFunction: null }, expansionColumnPosition: { classPropertyName: "expansionColumnPosition", publicName: "expansionColumnPosition", isSignal: true, isRequired: false, transformFunction: null }, expandedKeys: { classPropertyName: "expandedKeys", publicName: "expandedKeys", isSignal: true, isRequired: false, transformFunction: null }, expandableRow: { classPropertyName: "expandableRow", publicName: "expandableRow", isSignal: true, isRequired: false, transformFunction: null }, expansionLabel: { classPropertyName: "expansionLabel", publicName: "expansionLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sortChange: "sortChange", filterChange: "filterChange", pageChange: "pageChange", rowClick: "rowClick", selectionChange: "selectionChange", expansionChange: "expansionChange", columnResize: "columnResize" }, host: { properties: { "class": "this.hostClass", "attr.id": "this.hostId" } }, providers: [
1285
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DataTableComponent, isStandalone: true, selector: "fk-data-table", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, sortState: { classPropertyName: "sortState", publicName: "sortState", isSignal: true, isRequired: false, transformFunction: null }, filterState: { classPropertyName: "filterState", publicName: "filterState", isSignal: true, isRequired: false, transformFunction: null }, pageState: { classPropertyName: "pageState", publicName: "pageState", isSignal: true, isRequired: false, transformFunction: null }, rowSelection: { classPropertyName: "rowSelection", publicName: "rowSelection", isSignal: true, isRequired: false, transformFunction: null }, selectedKeys: { classPropertyName: "selectedKeys", publicName: "selectedKeys", isSignal: true, isRequired: false, transformFunction: null }, getRowId: { classPropertyName: "getRowId", publicName: "getRowId", isSignal: true, isRequired: false, transformFunction: null }, selectionDisabled: { classPropertyName: "selectionDisabled", publicName: "selectionDisabled", isSignal: true, isRequired: false, transformFunction: null }, selectionColumnPosition: { classPropertyName: "selectionColumnPosition", publicName: "selectionColumnPosition", isSignal: true, isRequired: false, transformFunction: null }, selectionLabel: { classPropertyName: "selectionLabel", publicName: "selectionLabel", isSignal: true, isRequired: false, transformFunction: null }, rowDetailTemplate: { classPropertyName: "rowDetailTemplate", publicName: "rowDetailTemplate", isSignal: true, isRequired: false, transformFunction: null }, expansionColumnPosition: { classPropertyName: "expansionColumnPosition", publicName: "expansionColumnPosition", isSignal: true, isRequired: false, transformFunction: null }, expandedKeys: { classPropertyName: "expandedKeys", publicName: "expandedKeys", isSignal: true, isRequired: false, transformFunction: null }, expandableRow: { classPropertyName: "expandableRow", publicName: "expandableRow", isSignal: true, isRequired: false, transformFunction: null }, expansionLabel: { classPropertyName: "expansionLabel", publicName: "expansionLabel", isSignal: true, isRequired: false, transformFunction: null }, groupHeadingRenderer: { classPropertyName: "groupHeadingRenderer", publicName: "groupHeadingRenderer", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sortChange: "sortChange", filterChange: "filterChange", pageChange: "pageChange", rowClick: "rowClick", selectionChange: "selectionChange", expansionChange: "expansionChange", columnResize: "columnResize" }, host: { properties: { "class": "this.hostClass", "attr.id": "this.hostId" } }, providers: [
1215
1286
  TableEngineService,
1216
1287
  VirtualizationEngineService,
1217
1288
  ResponsiveStrategyService,
1218
- ], viewQueries: [{ propertyName: "virtualViewport", first: true, predicate: ["virtualViewport"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (activeMode() === 'stack') {\n <!-- Stack path: mobile card view -->\n <fk-data-table-stack\n [rows]=\"engine.processedRows()\"\n [columns]=\"columns()\"\n [config]=\"mergedConfig()\"\n [trackByFn]=\"mergedConfig().trackBy\"\n [rowSelection]=\"rowSelection()\"\n [selectedKeys]=\"effectiveSelectedKeysForTemplate()\"\n [getRowId]=\"getRowId()\"\n [selectionDisabled]=\"selectionDisabled()\"\n [selectionLabel]=\"selectionLabel()\"\n (rowClick)=\"rowClick.emit($event)\"\n (toggleRow)=\"onToggleRow($event.row, $event.event)\"\n />\n} @else if (useVirtualization()) {\n <!-- Virtual path: div/grid-based with virtual scrolling -->\n <div role=\"table\" [attr.aria-label]=\"ariaLabel()\">\n @if (mergedConfig().showHeader) {\n <div class=\"fk-data-table__head\" role=\"rowgroup\">\n <div class=\"fk-data-table__head-row\" role=\"row\">\n @if (rowSelection() && selectionColumnPosition() === 'start') {\n <div\n class=\"fk-data-table__th fk-data-table__selection-cell\"\n role=\"columnheader\"\n >\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"\n headerSelectionState() === 'checked'\n ? 'Deselect all on page'\n : 'Select all on page'\n \"\n [checked]=\"headerSelectionState() === 'checked'\"\n [indeterminate]=\"headerSelectionState() === 'indeterminate'\"\n (click)=\"onToggleAllVisible($event)\"\n />\n </div>\n }\n @for (column of visibleColumns(); track column.id) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div\n class=\"fk-data-table__th\"\n role=\"columnheader\"\n [fkColumnResize]=\"column.id\"\n [enabled]=\"column.resizable === true\"\n (columnResize)=\"onColumnResize($event)\"\n [class.fk-data-table__th--sortable]=\"column.sortable\"\n [class.fk-data-table__th--align-center]=\"\n column.align === 'center'\n \"\n [class.fk-data-table__th--align-end]=\"column.align === 'end'\"\n [class.fk-data-table__th--pinned-start]=\"\n column.pinned === 'start'\n \"\n [class.fk-data-table__th--pinned-end]=\"column.pinned === 'end'\"\n [style.width]=\"column.width ?? null\"\n [style.min-width]=\"column.minWidth ?? null\"\n [style.max-width]=\"column.maxWidth ?? null\"\n [style.flex]=\"column.flex ?? null\"\n [style.--fk-data-table-pin-offset]=\"getPinOffset(column)\"\n [attr.aria-sort]=\"\n getSortDirection(column.id) === 'asc'\n ? 'ascending'\n : getSortDirection(column.id) === 'desc'\n ? 'descending'\n : null\n \"\n (click)=\"onHeaderClick(column)\"\n >\n @if (column.headerRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.headerRenderer\"\n [ngTemplateOutletContext]=\"{ $implicit: column }\"\n />\n } @else {\n {{ column.header }}\n }\n\n @if (column.sortable) {\n <span\n class=\"fk-data-table__sort-indicator\"\n [class.fk-data-table__sort-indicator--asc]=\"\n getSortDirection(column.id) === 'asc'\n \"\n [class.fk-data-table__sort-indicator--desc]=\"\n getSortDirection(column.id) === 'desc'\n \"\n aria-hidden=\"true\"\n >\n <svg\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 10 10\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path d=\"M5 2L8 6H2L5 2Z\" fill=\"currentColor\" />\n </svg>\n </span>\n }\n </div>\n }\n @if (rowSelection() && selectionColumnPosition() === 'end') {\n <div\n class=\"fk-data-table__th fk-data-table__selection-cell\"\n role=\"columnheader\"\n >\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"\n headerSelectionState() === 'checked'\n ? 'Deselect all on page'\n : 'Select all on page'\n \"\n [checked]=\"headerSelectionState() === 'checked'\"\n [indeterminate]=\"headerSelectionState() === 'indeterminate'\"\n (click)=\"onToggleAllVisible($event)\"\n />\n </div>\n }\n </div>\n </div>\n }\n\n <div\n #virtualViewport\n class=\"fk-data-table__viewport\"\n role=\"rowgroup\"\n (scroll)=\"onVirtualScroll($event)\"\n >\n <div\n class=\"fk-data-table__sentinel\"\n [style.height.px]=\"virtEngine.totalHeight()\"\n ></div>\n\n <div\n class=\"fk-data-table__virtual-body\"\n [style.transform]=\"'translateY(' + virtEngine.offsetY() + 'px)'\"\n >\n @for (\n row of virtualRows();\n track mergedConfig().trackBy(virtualStartIndex() + $index, row);\n let i = $index\n ) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div\n class=\"fk-data-table__row\"\n role=\"row\"\n [class.fk-data-table__row--selected]=\"isRowSelected(row)\"\n [style.height.px]=\"mergedConfig().rowHeight\"\n [ngStyle]=\"getRowStyle(row, virtualStartIndex() + i)\"\n (click)=\"onRowClick(row, virtualStartIndex() + i)\"\n >\n @if (rowSelection() && selectionColumnPosition() === 'start') {\n <div\n class=\"fk-data-table__td fk-data-table__selection-cell\"\n role=\"cell\"\n >\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"selectionLabel()\"\n [checked]=\"isRowSelected(row)\"\n [disabled]=\"isRowSelectionDisabled(row)\"\n (click)=\"onToggleRow(row, $event)\"\n />\n </div>\n }\n @for (column of visibleColumns(); track column.id) {\n <div\n class=\"fk-data-table__td\"\n role=\"cell\"\n [class.fk-data-table__td--truncate]=\"column.truncate\"\n [class.fk-data-table__td--align-center]=\"\n column.align === 'center'\n \"\n [class.fk-data-table__td--align-end]=\"column.align === 'end'\"\n [class.fk-data-table__td--pinned-start]=\"\n column.pinned === 'start'\n \"\n [class.fk-data-table__td--pinned-end]=\"column.pinned === 'end'\"\n [style.width]=\"column.width ?? null\"\n [style.min-width]=\"column.minWidth ?? null\"\n [style.max-width]=\"column.maxWidth ?? null\"\n [style.flex]=\"column.flex ?? null\"\n [style.--fk-data-table-pin-offset]=\"getPinOffset(column)\"\n >\n @if (column.cellRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.cellRenderer\"\n [ngTemplateOutletContext]=\"{\n $implicit: getCellValue(column, row),\n row: row,\n }\"\n />\n } @else {\n {{ getCellValue(column, row) }}\n }\n </div>\n }\n @if (rowSelection() && selectionColumnPosition() === 'end') {\n <div\n class=\"fk-data-table__td fk-data-table__selection-cell\"\n role=\"cell\"\n >\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"selectionLabel()\"\n [checked]=\"isRowSelected(row)\"\n [disabled]=\"isRowSelectionDisabled(row)\"\n (click)=\"onToggleRow(row, $event)\"\n />\n </div>\n }\n </div>\n }\n </div>\n </div>\n\n @if (isEmpty()) {\n <div class=\"fk-data-table__empty\" role=\"row\">\n <div role=\"cell\">{{ mergedConfig().emptyMessage }}</div>\n </div>\n }\n </div>\n} @else {\n <!-- Standard path: semantic table -->\n <div class=\"fk-data-table__scroll\">\n <table class=\"fk-data-table__table\" [attr.aria-label]=\"ariaLabel()\">\n @if (mergedConfig().showHeader) {\n <thead class=\"fk-data-table__head\">\n <tr class=\"fk-data-table__head-row\">\n @if (rowSelection() && selectionColumnPosition() === 'start') {\n <th class=\"fk-data-table__th fk-data-table__selection-cell\">\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"\n headerSelectionState() === 'checked'\n ? 'Deselect all on page'\n : 'Select all on page'\n \"\n [checked]=\"headerSelectionState() === 'checked'\"\n [indeterminate]=\"headerSelectionState() === 'indeterminate'\"\n (click)=\"onToggleAllVisible($event)\"\n />\n </th>\n }\n @if (hasExpansionStart()) {\n <th\n class=\"fk-data-table__th fk-data-table__expand-cell\"\n aria-hidden=\"true\"\n ></th>\n }\n @for (column of visibleColumns(); track column.id) {\n <th\n class=\"fk-data-table__th\"\n [fkColumnResize]=\"column.id\"\n [enabled]=\"column.resizable === true\"\n (columnResize)=\"onColumnResize($event)\"\n [class.fk-data-table__th--sortable]=\"column.sortable\"\n [class.fk-data-table__th--align-center]=\"\n column.align === 'center'\n \"\n [class.fk-data-table__th--align-end]=\"column.align === 'end'\"\n [class.fk-data-table__th--pinned-start]=\"\n column.pinned === 'start'\n \"\n [class.fk-data-table__th--pinned-end]=\"column.pinned === 'end'\"\n [style.width]=\"column.width ?? null\"\n [style.min-width]=\"column.minWidth ?? null\"\n [style.max-width]=\"column.maxWidth ?? null\"\n [style.--fk-data-table-pin-offset]=\"getPinOffset(column)\"\n [attr.aria-sort]=\"\n getSortDirection(column.id) === 'asc'\n ? 'ascending'\n : getSortDirection(column.id) === 'desc'\n ? 'descending'\n : null\n \"\n (click)=\"onHeaderClick(column)\"\n >\n @if (column.headerRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.headerRenderer\"\n [ngTemplateOutletContext]=\"{ $implicit: column }\"\n />\n } @else {\n {{ column.header }}\n }\n\n @if (column.sortable) {\n <span\n class=\"fk-data-table__sort-indicator\"\n [class.fk-data-table__sort-indicator--asc]=\"\n getSortDirection(column.id) === 'asc'\n \"\n [class.fk-data-table__sort-indicator--desc]=\"\n getSortDirection(column.id) === 'desc'\n \"\n aria-hidden=\"true\"\n >\n <svg\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 10 10\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path d=\"M5 2L8 6H2L5 2Z\" fill=\"currentColor\" />\n </svg>\n </span>\n }\n </th>\n }\n @if (rowSelection() && selectionColumnPosition() === 'end') {\n <th class=\"fk-data-table__th fk-data-table__selection-cell\">\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"\n headerSelectionState() === 'checked'\n ? 'Deselect all on page'\n : 'Select all on page'\n \"\n [checked]=\"headerSelectionState() === 'checked'\"\n [indeterminate]=\"headerSelectionState() === 'indeterminate'\"\n (click)=\"onToggleAllVisible($event)\"\n />\n </th>\n }\n @if (hasExpansionEnd()) {\n <th\n class=\"fk-data-table__th fk-data-table__expand-cell\"\n aria-hidden=\"true\"\n ></th>\n }\n </tr>\n </thead>\n }\n\n <tbody class=\"fk-data-table__body\">\n @for (\n row of engine.processedRows();\n track mergedConfig().trackBy($index, row);\n let i = $index\n ) {\n <tr\n class=\"fk-data-table__row\"\n [class.fk-data-table__row--selected]=\"isRowSelected(row)\"\n [class.fk-data-table__row--expanded]=\"\n hasExpansion() && isRowExpanded(row)\n \"\n [ngStyle]=\"getRowStyle(row, i)\"\n (click)=\"onRowClick(row, i)\"\n >\n @if (rowSelection() && selectionColumnPosition() === 'start') {\n <td class=\"fk-data-table__td fk-data-table__selection-cell\">\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"selectionLabel()\"\n [checked]=\"isRowSelected(row)\"\n [disabled]=\"isRowSelectionDisabled(row)\"\n (click)=\"onToggleRow(row, $event)\"\n />\n </td>\n }\n @if (hasExpansionStart()) {\n <td class=\"fk-data-table__td fk-data-table__expand-cell\">\n @if (isRowExpandable(row)) {\n <button\n type=\"button\"\n class=\"fk-data-table__expand-button\"\n [class.fk-data-table__expand-button--expanded]=\"\n isRowExpanded(row)\n \"\n [attr.aria-label]=\"expansionLabel()\"\n [attr.aria-expanded]=\"isRowExpanded(row)\"\n (click)=\"onToggleExpand(row, $event)\"\n >\n <svg\n class=\"fk-data-table__expand-chevron\"\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 10 10\"\n fill=\"none\"\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M3 1.5L6.5 5L3 8.5\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n }\n </td>\n }\n @for (column of visibleColumns(); track column.id) {\n <td\n class=\"fk-data-table__td\"\n [class.fk-data-table__td--truncate]=\"column.truncate\"\n [class.fk-data-table__td--align-center]=\"\n column.align === 'center'\n \"\n [class.fk-data-table__td--align-end]=\"column.align === 'end'\"\n [class.fk-data-table__td--pinned-start]=\"\n column.pinned === 'start'\n \"\n [class.fk-data-table__td--pinned-end]=\"column.pinned === 'end'\"\n [style.--fk-data-table-pin-offset]=\"getPinOffset(column)\"\n >\n @if (column.cellRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.cellRenderer\"\n [ngTemplateOutletContext]=\"{\n $implicit: getCellValue(column, row),\n row: row,\n }\"\n />\n } @else {\n {{ getCellValue(column, row) }}\n }\n </td>\n }\n @if (rowSelection() && selectionColumnPosition() === 'end') {\n <td class=\"fk-data-table__td fk-data-table__selection-cell\">\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"selectionLabel()\"\n [checked]=\"isRowSelected(row)\"\n [disabled]=\"isRowSelectionDisabled(row)\"\n (click)=\"onToggleRow(row, $event)\"\n />\n </td>\n }\n @if (hasExpansionEnd()) {\n <td class=\"fk-data-table__td fk-data-table__expand-cell\">\n @if (isRowExpandable(row)) {\n <button\n type=\"button\"\n class=\"fk-data-table__expand-button\"\n [class.fk-data-table__expand-button--expanded]=\"\n isRowExpanded(row)\n \"\n [attr.aria-label]=\"expansionLabel()\"\n [attr.aria-expanded]=\"isRowExpanded(row)\"\n (click)=\"onToggleExpand(row, $event)\"\n >\n <svg\n class=\"fk-data-table__expand-chevron\"\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 10 10\"\n fill=\"none\"\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M3 1.5L6.5 5L3 8.5\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n }\n </td>\n }\n </tr>\n @if (hasExpansion() && isRowExpanded(row)) {\n <tr class=\"fk-data-table__detail-row\">\n <td\n class=\"fk-data-table__detail-cell\"\n [attr.colspan]=\"totalColumnCount()\"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowDetailTemplate()!\"\n [ngTemplateOutletContext]=\"{\n $implicit: row,\n row: row,\n index: i,\n }\"\n />\n </td>\n </tr>\n }\n }\n </tbody>\n </table>\n\n @if (isEmpty()) {\n <div class=\"fk-data-table__empty\">\n {{ mergedConfig().emptyMessage }}\n </div>\n }\n </div>\n}\n\n@if (loading()) {\n <div class=\"fk-data-table__loading-overlay\" aria-live=\"polite\">\n <span class=\"fk-data-table__loading-text\">Loading\u2026</span>\n </div>\n}\n", styles: [":host{display:block;position:relative;border:var(--fk-data-table-border-width, 1px) solid var(--fk-data-table-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-data-table-border-radius, var(--fk-radius-md, .375rem));overflow:hidden}.fk-data-table__scroll{overflow-x:auto;width:100%;max-height:var(--fk-data-table-viewport-height)}.fk-data-table__table{width:100%;border-collapse:collapse;border-spacing:0}.fk-data-table__head{background:var(--fk-data-table-head-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-data-table__head-row{display:flex}.fk-data-table__table .fk-data-table__head-row{display:table-row}.fk-data-table__th{position:relative;padding:var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem));text-align:start;font-weight:var(--fk-data-table-head-font-weight, var(--fk-font-weight-semibold, 600));font-size:var(--fk-data-table-head-font-size, var(--fk-typography-small-font-size, .875rem));color:var(--fk-data-table-head-color, var(--fk-color-text-muted, #6b7a8d));border-bottom:1px solid var(--fk-data-table-border-color, var(--fk-color-border, #d9e2ee));white-space:nowrap;-webkit-user-select:none;user-select:none;flex:1}.fk-data-table__th--sortable{cursor:pointer}.fk-data-table__th--sortable:hover{color:var(--fk-data-table-head-hover-color, var(--fk-color-text, #1f2d3d))}.fk-data-table__th--align-center{text-align:center}.fk-data-table__th--align-end{text-align:end}.fk-data-table__table .fk-data-table__th{flex:none}.fk-data-table__sort-indicator{display:inline-flex;margin-inline-start:var(--fk-rhythm-1, .25rem);opacity:.3;transition:transform .15s ease,opacity .15s ease}.fk-data-table__sort-indicator--asc{opacity:1;transform:rotate(0)}.fk-data-table__sort-indicator--desc{opacity:1;transform:rotate(180deg)}.fk-data-table__viewport{overflow-y:auto;position:relative;max-height:var(--fk-data-table-viewport-height, 600px)}.fk-data-table__sentinel{position:absolute;top:0;left:0;width:1px;visibility:hidden}.fk-data-table__virtual-body{position:relative;will-change:transform}.fk-data-table__body{background:var(--fk-data-table-body-bg, var(--fk-color-surface, #ffffff))}.fk-data-table__row{display:flex;border-bottom:1px solid var(--fk-data-table-border-color, var(--fk-color-border, #d9e2ee));cursor:pointer}.fk-data-table__row:hover{background:var(--fk-data-table-row-hover-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-data-table__row:last-child{border-bottom:none}.fk-data-table__table .fk-data-table__row{display:table-row}.fk-data-table__td{padding:var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem));font-size:var(--fk-data-table-body-font-size, var(--fk-typography-body-font-size, .875rem));color:var(--fk-data-table-body-color, var(--fk-color-text, #1f2d3d));vertical-align:middle;flex:1}.fk-data-table__td--truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fk-data-table__td--align-center{text-align:center}.fk-data-table__td--align-end{text-align:end}.fk-data-table__table .fk-data-table__td{flex:none}.fk-data-table__empty{padding:var(--fk-data-table-empty-padding, var(--fk-rhythm-8, 2rem));text-align:center;color:var(--fk-data-table-empty-color, var(--fk-color-text-muted, #6b7a8d));font-size:var(--fk-data-table-body-font-size, var(--fk-typography-body-font-size, .875rem))}.fk-data-table__loading-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:var(--fk-data-table-loading-bg, var(--fk-color-surface-overlay, rgba(255, 255, 255, .7)));z-index:1}.fk-data-table__loading-text{font-size:var(--fk-data-table-body-font-size, var(--fk-typography-body-font-size, .875rem));color:var(--fk-data-table-loading-color, var(--fk-color-text-muted, #6b7a8d))}.fk-data-table__resize-handle{position:absolute;top:0;right:0;bottom:0;width:var(--fk-data-table-resize-handle-width, 4px);cursor:col-resize;background:transparent;z-index:1}.fk-data-table__resize-handle:hover,.fk-data-table__resize-handle:active{background:var(--fk-data-table-resize-handle-color, var(--fk-color-primary, #3b82f6))}.fk-data-table__th--pinned-start,.fk-data-table__td--pinned-start{position:sticky;left:var(--fk-data-table-pin-offset, 0);z-index:2;background:inherit}.fk-data-table__th--pinned-end,.fk-data-table__td--pinned-end{position:sticky;right:var(--fk-data-table-pin-offset, 0);z-index:2;background:inherit}.fk-data-table__selection-cell{flex:none;width:var(--fk-data-table-selection-cell-width, 2.75rem);min-width:var(--fk-data-table-selection-cell-width, 2.75rem);max-width:var(--fk-data-table-selection-cell-width, 2.75rem);text-align:center;cursor:default}.fk-data-table__selection-cell:hover{color:inherit}.fk-data-table__row--selected{background:var(--fk-data-table-row-selected-bg, var(--fk-color-primary-light, #eaf4ff))}.fk-data-table__row--selected:hover{background:var(--fk-data-table-row-selected-hover-bg, var(--fk-color-primary-light, #dbe9ff))}.fk-data-table__expand-cell{flex:none;width:var(--fk-data-table-expand-cell-width, 2.5rem);min-width:var(--fk-data-table-expand-cell-width, 2.5rem);max-width:var(--fk-data-table-expand-cell-width, 2.5rem);text-align:center;cursor:default;padding-inline:var(--fk-rhythm-1, .25rem)}.fk-data-table__expand-button{appearance:none;display:inline-flex;align-items:center;justify-content:center;width:var(--fk-data-table-expand-button-size, 1.5rem);height:var(--fk-data-table-expand-button-size, 1.5rem);padding:0;background:transparent;border:1px solid transparent;border-radius:var(--fk-data-table-expand-button-radius, .25rem);color:var(--fk-data-table-expand-color, var(--fk-color-text-muted, #5d6b80));cursor:pointer;transition:background-color .15s ease,color .15s ease,transform .15s ease}.fk-data-table__expand-button:hover{background:var(--fk-data-table-expand-button-hover-bg, var(--fk-color-surface-muted, #f1f4f8));color:var(--fk-data-table-expand-color-hover, var(--fk-color-text, #1c2433))}.fk-data-table__expand-button:focus-visible{outline:2px solid var(--fk-data-table-expand-focus-ring, var(--fk-color-primary, #0066cc));outline-offset:2px}.fk-data-table__expand-button--expanded{color:var(--fk-data-table-expand-color-active, var(--fk-color-primary, #0066cc))}.fk-data-table__expand-button--expanded .fk-data-table__expand-chevron{transform:rotate(90deg)}.fk-data-table__expand-chevron{transition:transform .15s ease}.fk-data-table__row--expanded{background:var(--fk-data-table-row-expanded-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-data-table__detail-row{background:var(--fk-data-table-detail-row-bg, var(--fk-color-surface, #ffffff))}.fk-data-table__detail-cell{padding:var(--fk-data-table-detail-cell-padding, var(--fk-rhythm-4, 1rem));border-top:1px solid var(--fk-data-table-detail-border-color, var(--fk-color-border, #d9e2ee));border-bottom:1px solid var(--fk-data-table-detail-border-color, var(--fk-color-border, #d9e2ee))}.fk-data-table__checkbox{appearance:none;width:var(--fk-data-table-checkbox-size, 1rem);height:var(--fk-data-table-checkbox-size, 1rem);margin:0;border:1px solid var(--fk-data-table-checkbox-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-data-table-checkbox-radius, .25rem);background:var(--fk-data-table-checkbox-bg, var(--fk-color-surface, #ffffff));cursor:pointer;transition:background-color .15s ease,border-color .15s ease,box-shadow .15s ease;position:relative;vertical-align:middle}.fk-data-table__checkbox:focus-visible{outline:none;box-shadow:var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18))}.fk-data-table__checkbox:hover:not(:disabled){border-color:var(--fk-data-table-checkbox-hover-border-color, var(--fk-color-primary, #0a84ff))}.fk-data-table__checkbox:checked,.fk-data-table__checkbox:indeterminate{background:var(--fk-data-table-checkbox-checked-bg, var(--fk-color-primary, #0a84ff));border-color:var(--fk-data-table-checkbox-checked-border-color, var(--fk-color-primary, #0a84ff))}.fk-data-table__checkbox:checked:after{content:\"\";position:absolute;inset:0;background:var(--fk-data-table-checkbox-icon-color, var(--fk-white, #ffffff));mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='currentColor' d='M6.5 11L3 7.5l1-1L6.5 9 12 3.5l1 1z'/></svg>\") center/75% 75% no-repeat;-webkit-mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='currentColor' d='M6.5 11L3 7.5l1-1L6.5 9 12 3.5l1 1z'/></svg>\") center/75% 75% no-repeat}.fk-data-table__checkbox:indeterminate:after{content:\"\";position:absolute;inset:0;background:var(--fk-data-table-checkbox-icon-color, var(--fk-white, #ffffff));mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><rect x='3' y='7' width='10' height='2' rx='1' fill='currentColor'/></svg>\") center/75% 75% no-repeat;-webkit-mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><rect x='3' y='7' width='10' height='2' rx='1' fill='currentColor'/></svg>\") center/75% 75% no-repeat}.fk-data-table__checkbox:disabled{cursor:not-allowed;opacity:.5}.fk-data-table--resizing{cursor:col-resize;-webkit-user-select:none;user-select:none}.fk-data-table--sticky-header .fk-data-table__head{position:sticky;top:0;z-index:1}.fk-data-table--sticky-header .fk-data-table__table .fk-data-table__th{position:sticky;top:0;z-index:1;background:var(--fk-data-table-head-bg, var(--fk-color-surface-muted, #f7f9fb))}\n"], dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: DataTableStackComponent, selector: "fk-data-table-stack", inputs: ["rows", "columns", "config", "trackByFn", "rowSelection", "selectedKeys", "getRowId", "selectionDisabled", "selectionLabel"], outputs: ["rowClick", "toggleRow"] }, { kind: "directive", type: ColumnResizeDirective, selector: "[fkColumnResize]", inputs: ["fkColumnResize", "enabled", "minWidth", "maxWidth"], outputs: ["columnResize"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1289
+ ], viewQueries: [{ propertyName: "virtualViewport", first: true, predicate: ["virtualViewport"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (activeMode() === 'stack') {\n <!-- Stack path: mobile card view -->\n <fk-data-table-stack\n [rows]=\"engine.processedRows()\"\n [columns]=\"columns()\"\n [config]=\"mergedConfig()\"\n [trackByFn]=\"mergedConfig().trackBy\"\n [rowSelection]=\"rowSelection()\"\n [selectedKeys]=\"effectiveSelectedKeysForTemplate()\"\n [getRowId]=\"getRowId()\"\n [selectionDisabled]=\"selectionDisabled()\"\n [selectionLabel]=\"selectionLabel()\"\n [groupHeadingRenderer]=\"groupHeadingRenderer()\"\n (rowClick)=\"rowClick.emit($event)\"\n (toggleRow)=\"onToggleRow($event.row, $event.event)\"\n />\n} @else if (useVirtualization()) {\n <!-- Virtual path: div/grid-based with virtual scrolling -->\n <div role=\"table\" [attr.aria-label]=\"ariaLabel()\">\n @if (mergedConfig().showHeader) {\n <div class=\"fk-data-table__head\" role=\"rowgroup\">\n <div class=\"fk-data-table__head-row\" role=\"row\">\n @if (rowSelection() && selectionColumnPosition() === 'start') {\n <div\n class=\"fk-data-table__th fk-data-table__selection-cell\"\n role=\"columnheader\"\n >\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"\n headerSelectionState() === 'checked'\n ? 'Deselect all on page'\n : 'Select all on page'\n \"\n [checked]=\"headerSelectionState() === 'checked'\"\n [indeterminate]=\"headerSelectionState() === 'indeterminate'\"\n (click)=\"onToggleAllVisible($event)\"\n />\n </div>\n }\n @for (column of visibleColumns(); track column.id) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div\n class=\"fk-data-table__th\"\n role=\"columnheader\"\n [fkColumnResize]=\"column.id\"\n [enabled]=\"column.resizable === true\"\n (columnResize)=\"onColumnResize($event)\"\n [class.fk-data-table__th--sortable]=\"column.sortable\"\n [class.fk-data-table__th--align-center]=\"\n column.align === 'center'\n \"\n [class.fk-data-table__th--align-end]=\"column.align === 'end'\"\n [class.fk-data-table__th--pinned-start]=\"\n column.pinned === 'start'\n \"\n [class.fk-data-table__th--pinned-end]=\"column.pinned === 'end'\"\n [style.width]=\"column.width ?? null\"\n [style.min-width]=\"column.minWidth ?? null\"\n [style.max-width]=\"column.maxWidth ?? null\"\n [style.flex]=\"column.flex ?? null\"\n [style.--fk-data-table-pin-offset]=\"getPinOffset(column)\"\n [attr.aria-sort]=\"\n getSortDirection(column.id) === 'asc'\n ? 'ascending'\n : getSortDirection(column.id) === 'desc'\n ? 'descending'\n : null\n \"\n (click)=\"onHeaderClick(column)\"\n >\n @if (column.headerRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.headerRenderer\"\n [ngTemplateOutletContext]=\"{ $implicit: column }\"\n />\n } @else {\n {{ column.header }}\n }\n\n @if (column.sortable) {\n <span\n class=\"fk-data-table__sort-indicator\"\n [class.fk-data-table__sort-indicator--asc]=\"\n getSortDirection(column.id) === 'asc'\n \"\n [class.fk-data-table__sort-indicator--desc]=\"\n getSortDirection(column.id) === 'desc'\n \"\n aria-hidden=\"true\"\n >\n <svg\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 10 10\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path d=\"M5 2L8 6H2L5 2Z\" fill=\"currentColor\" />\n </svg>\n </span>\n }\n </div>\n }\n @if (rowSelection() && selectionColumnPosition() === 'end') {\n <div\n class=\"fk-data-table__th fk-data-table__selection-cell\"\n role=\"columnheader\"\n >\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"\n headerSelectionState() === 'checked'\n ? 'Deselect all on page'\n : 'Select all on page'\n \"\n [checked]=\"headerSelectionState() === 'checked'\"\n [indeterminate]=\"headerSelectionState() === 'indeterminate'\"\n (click)=\"onToggleAllVisible($event)\"\n />\n </div>\n }\n </div>\n </div>\n }\n\n <div\n #virtualViewport\n class=\"fk-data-table__viewport\"\n role=\"rowgroup\"\n (scroll)=\"onVirtualScroll($event)\"\n >\n <div\n class=\"fk-data-table__sentinel\"\n [style.height.px]=\"virtEngine.totalHeight()\"\n ></div>\n\n <div\n class=\"fk-data-table__virtual-body\"\n [style.transform]=\"'translateY(' + virtEngine.offsetY() + 'px)'\"\n >\n @for (\n row of virtualRows();\n track mergedConfig().trackBy(virtualStartIndex() + $index, row);\n let i = $index\n ) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div\n class=\"fk-data-table__row\"\n role=\"row\"\n [class.fk-data-table__row--selected]=\"isRowSelected(row)\"\n [style.height.px]=\"mergedConfig().rowHeight\"\n [ngStyle]=\"getRowStyle(row, virtualStartIndex() + i)\"\n (click)=\"onRowClick(row, virtualStartIndex() + i)\"\n >\n @if (rowSelection() && selectionColumnPosition() === 'start') {\n <div\n class=\"fk-data-table__td fk-data-table__selection-cell\"\n role=\"cell\"\n >\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"selectionLabel()\"\n [checked]=\"isRowSelected(row)\"\n [disabled]=\"isRowSelectionDisabled(row)\"\n (click)=\"onToggleRow(row, $event)\"\n />\n </div>\n }\n @for (column of visibleColumns(); track column.id) {\n <div\n class=\"fk-data-table__td\"\n role=\"cell\"\n [class.fk-data-table__td--truncate]=\"column.truncate\"\n [class.fk-data-table__td--align-center]=\"\n column.align === 'center'\n \"\n [class.fk-data-table__td--align-end]=\"column.align === 'end'\"\n [class.fk-data-table__td--pinned-start]=\"\n column.pinned === 'start'\n \"\n [class.fk-data-table__td--pinned-end]=\"column.pinned === 'end'\"\n [style.width]=\"column.width ?? null\"\n [style.min-width]=\"column.minWidth ?? null\"\n [style.max-width]=\"column.maxWidth ?? null\"\n [style.flex]=\"column.flex ?? null\"\n [style.--fk-data-table-pin-offset]=\"getPinOffset(column)\"\n >\n @if (column.cellRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.cellRenderer\"\n [ngTemplateOutletContext]=\"{\n $implicit: getCellValue(column, row),\n row: row,\n }\"\n />\n } @else {\n {{ getCellValue(column, row) }}\n }\n </div>\n }\n @if (rowSelection() && selectionColumnPosition() === 'end') {\n <div\n class=\"fk-data-table__td fk-data-table__selection-cell\"\n role=\"cell\"\n >\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"selectionLabel()\"\n [checked]=\"isRowSelected(row)\"\n [disabled]=\"isRowSelectionDisabled(row)\"\n (click)=\"onToggleRow(row, $event)\"\n />\n </div>\n }\n </div>\n }\n </div>\n </div>\n\n @if (isEmpty()) {\n <div class=\"fk-data-table__empty\" role=\"row\">\n <div role=\"cell\">{{ mergedConfig().emptyMessage }}</div>\n </div>\n }\n </div>\n} @else {\n <!-- Standard path: semantic table -->\n <div class=\"fk-data-table__scroll\">\n <table class=\"fk-data-table__table\" [attr.aria-label]=\"ariaLabel()\">\n @if (mergedConfig().showHeader) {\n <thead class=\"fk-data-table__head\">\n <tr class=\"fk-data-table__head-row\">\n @if (rowSelection() && selectionColumnPosition() === 'start') {\n <th class=\"fk-data-table__th fk-data-table__selection-cell\">\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"\n headerSelectionState() === 'checked'\n ? 'Deselect all on page'\n : 'Select all on page'\n \"\n [checked]=\"headerSelectionState() === 'checked'\"\n [indeterminate]=\"headerSelectionState() === 'indeterminate'\"\n (click)=\"onToggleAllVisible($event)\"\n />\n </th>\n }\n @if (hasExpansionStart()) {\n <th\n class=\"fk-data-table__th fk-data-table__expand-cell\"\n aria-hidden=\"true\"\n ></th>\n }\n @for (column of visibleColumns(); track column.id) {\n <th\n class=\"fk-data-table__th\"\n [fkColumnResize]=\"column.id\"\n [enabled]=\"column.resizable === true\"\n (columnResize)=\"onColumnResize($event)\"\n [class.fk-data-table__th--sortable]=\"column.sortable\"\n [class.fk-data-table__th--align-center]=\"\n column.align === 'center'\n \"\n [class.fk-data-table__th--align-end]=\"column.align === 'end'\"\n [class.fk-data-table__th--pinned-start]=\"\n column.pinned === 'start'\n \"\n [class.fk-data-table__th--pinned-end]=\"column.pinned === 'end'\"\n [style.width]=\"column.width ?? null\"\n [style.min-width]=\"column.minWidth ?? null\"\n [style.max-width]=\"column.maxWidth ?? null\"\n [style.--fk-data-table-pin-offset]=\"getPinOffset(column)\"\n [attr.aria-sort]=\"\n getSortDirection(column.id) === 'asc'\n ? 'ascending'\n : getSortDirection(column.id) === 'desc'\n ? 'descending'\n : null\n \"\n (click)=\"onHeaderClick(column)\"\n >\n @if (column.headerRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.headerRenderer\"\n [ngTemplateOutletContext]=\"{ $implicit: column }\"\n />\n } @else {\n {{ column.header }}\n }\n\n @if (column.sortable) {\n <span\n class=\"fk-data-table__sort-indicator\"\n [class.fk-data-table__sort-indicator--asc]=\"\n getSortDirection(column.id) === 'asc'\n \"\n [class.fk-data-table__sort-indicator--desc]=\"\n getSortDirection(column.id) === 'desc'\n \"\n aria-hidden=\"true\"\n >\n <svg\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 10 10\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path d=\"M5 2L8 6H2L5 2Z\" fill=\"currentColor\" />\n </svg>\n </span>\n }\n </th>\n }\n @if (rowSelection() && selectionColumnPosition() === 'end') {\n <th class=\"fk-data-table__th fk-data-table__selection-cell\">\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"\n headerSelectionState() === 'checked'\n ? 'Deselect all on page'\n : 'Select all on page'\n \"\n [checked]=\"headerSelectionState() === 'checked'\"\n [indeterminate]=\"headerSelectionState() === 'indeterminate'\"\n (click)=\"onToggleAllVisible($event)\"\n />\n </th>\n }\n @if (hasExpansionEnd()) {\n <th\n class=\"fk-data-table__th fk-data-table__expand-cell\"\n aria-hidden=\"true\"\n ></th>\n }\n </tr>\n </thead>\n }\n\n <tbody class=\"fk-data-table__body\">\n @for (\n row of engine.processedRows();\n track mergedConfig().trackBy($index, row);\n let i = $index\n ) {\n <!--\n A heading opens each run of rows sharing a group key. Emitted from\n inside the row loop rather than by pre-grouping the rows, so the\n table still renders exactly the rows it was given, in the order it\n was given them.\n -->\n @if (groupHeadingFor(row, i); as heading) {\n <tr class=\"fk-data-table__group-heading\">\n <th\n scope=\"colgroup\"\n class=\"fk-data-table__group-heading-cell\"\n [attr.colspan]=\"groupHeadingSpan()\"\n >\n @if (groupHeadingRenderer()) {\n <ng-container\n [ngTemplateOutlet]=\"groupHeadingRenderer()!\"\n [ngTemplateOutletContext]=\"{ $implicit: heading, row: row }\"\n />\n } @else {\n {{ heading }}\n }\n </th>\n </tr>\n }\n\n <tr\n class=\"fk-data-table__row\"\n [class.fk-data-table__row--selected]=\"isRowSelected(row)\"\n [class.fk-data-table__row--expanded]=\"\n hasExpansion() && isRowExpanded(row)\n \"\n [ngStyle]=\"getRowStyle(row, i)\"\n (click)=\"onRowClick(row, i)\"\n >\n @if (rowSelection() && selectionColumnPosition() === 'start') {\n <td class=\"fk-data-table__td fk-data-table__selection-cell\">\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"selectionLabel()\"\n [checked]=\"isRowSelected(row)\"\n [disabled]=\"isRowSelectionDisabled(row)\"\n (click)=\"onToggleRow(row, $event)\"\n />\n </td>\n }\n @if (hasExpansionStart()) {\n <td class=\"fk-data-table__td fk-data-table__expand-cell\">\n @if (isRowExpandable(row)) {\n <button\n type=\"button\"\n class=\"fk-data-table__expand-button\"\n [class.fk-data-table__expand-button--expanded]=\"\n isRowExpanded(row)\n \"\n [attr.aria-label]=\"expansionLabel()\"\n [attr.aria-expanded]=\"isRowExpanded(row)\"\n (click)=\"onToggleExpand(row, $event)\"\n >\n <svg\n class=\"fk-data-table__expand-chevron\"\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 10 10\"\n fill=\"none\"\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M3 1.5L6.5 5L3 8.5\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n }\n </td>\n }\n @for (column of visibleColumns(); track column.id) {\n <td\n class=\"fk-data-table__td\"\n [class.fk-data-table__td--truncate]=\"column.truncate\"\n [class.fk-data-table__td--align-center]=\"\n column.align === 'center'\n \"\n [class.fk-data-table__td--align-end]=\"column.align === 'end'\"\n [class.fk-data-table__td--pinned-start]=\"\n column.pinned === 'start'\n \"\n [class.fk-data-table__td--pinned-end]=\"column.pinned === 'end'\"\n [style.--fk-data-table-pin-offset]=\"getPinOffset(column)\"\n >\n @if (column.cellRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.cellRenderer\"\n [ngTemplateOutletContext]=\"{\n $implicit: getCellValue(column, row),\n row: row,\n }\"\n />\n } @else {\n {{ getCellValue(column, row) }}\n }\n </td>\n }\n @if (rowSelection() && selectionColumnPosition() === 'end') {\n <td class=\"fk-data-table__td fk-data-table__selection-cell\">\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"selectionLabel()\"\n [checked]=\"isRowSelected(row)\"\n [disabled]=\"isRowSelectionDisabled(row)\"\n (click)=\"onToggleRow(row, $event)\"\n />\n </td>\n }\n @if (hasExpansionEnd()) {\n <td class=\"fk-data-table__td fk-data-table__expand-cell\">\n @if (isRowExpandable(row)) {\n <button\n type=\"button\"\n class=\"fk-data-table__expand-button\"\n [class.fk-data-table__expand-button--expanded]=\"\n isRowExpanded(row)\n \"\n [attr.aria-label]=\"expansionLabel()\"\n [attr.aria-expanded]=\"isRowExpanded(row)\"\n (click)=\"onToggleExpand(row, $event)\"\n >\n <svg\n class=\"fk-data-table__expand-chevron\"\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 10 10\"\n fill=\"none\"\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M3 1.5L6.5 5L3 8.5\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n }\n </td>\n }\n </tr>\n @if (hasExpansion() && isRowExpanded(row)) {\n <tr class=\"fk-data-table__detail-row\">\n <td\n class=\"fk-data-table__detail-cell\"\n [attr.colspan]=\"totalColumnCount()\"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowDetailTemplate()!\"\n [ngTemplateOutletContext]=\"{\n $implicit: row,\n row: row,\n index: i,\n }\"\n />\n </td>\n </tr>\n }\n }\n </tbody>\n </table>\n\n @if (isEmpty()) {\n <div class=\"fk-data-table__empty\">\n {{ mergedConfig().emptyMessage }}\n </div>\n }\n </div>\n}\n\n@if (loading()) {\n <div class=\"fk-data-table__loading-overlay\" aria-live=\"polite\">\n <span class=\"fk-data-table__loading-text\">Loading\u2026</span>\n </div>\n}\n", styles: [":host{display:block;position:relative;border:var(--fk-data-table-border-width, 1px) solid var(--fk-data-table-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-data-table-border-radius, var(--fk-radius-md, .375rem));overflow:hidden}.fk-data-table__scroll{overflow-x:auto;width:100%;max-height:var(--fk-data-table-viewport-height)}.fk-data-table__table{width:100%;border-collapse:collapse;border-spacing:0}.fk-data-table__head{background:var(--fk-data-table-head-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-data-table__head-row{display:flex}.fk-data-table__table .fk-data-table__head-row{display:table-row}.fk-data-table__th{position:relative;padding:var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem));text-align:start;font-weight:var(--fk-data-table-head-font-weight, var(--fk-font-weight-semibold, 600));font-size:var(--fk-data-table-head-font-size, var(--fk-typography-small-font-size, .875rem));color:var(--fk-data-table-head-color, var(--fk-color-text-muted, #6b7a8d));border-bottom:1px solid var(--fk-data-table-border-color, var(--fk-color-border, #d9e2ee));white-space:nowrap;-webkit-user-select:none;user-select:none;flex:1}.fk-data-table__th--sortable{cursor:pointer}.fk-data-table__th--sortable:hover{color:var(--fk-data-table-head-hover-color, var(--fk-color-text, #1f2d3d))}.fk-data-table__th--align-center{text-align:center}.fk-data-table__th--align-end{text-align:end}.fk-data-table__table .fk-data-table__th{flex:none}.fk-data-table__sort-indicator{display:inline-flex;margin-inline-start:var(--fk-rhythm-1, .25rem);opacity:.3;transition:transform .15s ease,opacity .15s ease}.fk-data-table__sort-indicator--asc{opacity:1;transform:rotate(0)}.fk-data-table__sort-indicator--desc{opacity:1;transform:rotate(180deg)}.fk-data-table__viewport{overflow-y:auto;position:relative;max-height:var(--fk-data-table-viewport-height, 600px)}.fk-data-table__sentinel{position:absolute;top:0;left:0;width:1px;visibility:hidden}.fk-data-table__virtual-body{position:relative;will-change:transform}.fk-data-table__group-heading{background:var(--fk-data-table-group-heading-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-data-table__group-heading-cell{padding:var(--fk-data-table-group-heading-padding, var(--fk-rhythm-2, .5rem) var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem)));text-align:start;font-size:var(--fk-data-table-group-heading-font-size, var(--fk-typography-caption-font-size, .75rem));font-weight:var(--fk-data-table-group-heading-font-weight, var(--fk-font-weight-semibold, 600));letter-spacing:var(--fk-data-table-group-heading-letter-spacing, .04em);text-transform:var(--fk-data-table-group-heading-text-transform, uppercase);color:var(--fk-data-table-group-heading-color, var(--fk-color-text-muted, #6b7a8d));border-block:1px solid var(--fk-data-table-border-color, var(--fk-color-border, #d9e2ee))}.fk-data-table__body{background:var(--fk-data-table-body-bg, var(--fk-color-surface, #ffffff))}.fk-data-table__row{display:flex;border-bottom:1px solid var(--fk-data-table-border-color, var(--fk-color-border, #d9e2ee));cursor:pointer}.fk-data-table__row:hover{background:var(--fk-data-table-row-hover-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-data-table__row:last-child{border-bottom:none}.fk-data-table__table .fk-data-table__row{display:table-row}.fk-data-table__td{padding:var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem));font-size:var(--fk-data-table-body-font-size, var(--fk-typography-body-font-size, .875rem));color:var(--fk-data-table-body-color, var(--fk-color-text, #1f2d3d));vertical-align:middle;flex:1}.fk-data-table__td--truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fk-data-table__td--align-center{text-align:center}.fk-data-table__td--align-end{text-align:end}.fk-data-table__table .fk-data-table__td{flex:none}.fk-data-table__empty{padding:var(--fk-data-table-empty-padding, var(--fk-rhythm-8, 2rem));text-align:center;color:var(--fk-data-table-empty-color, var(--fk-color-text-muted, #6b7a8d));font-size:var(--fk-data-table-body-font-size, var(--fk-typography-body-font-size, .875rem))}.fk-data-table__loading-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:var(--fk-data-table-loading-bg, var(--fk-color-surface-overlay, rgba(255, 255, 255, .7)));z-index:1}.fk-data-table__loading-text{font-size:var(--fk-data-table-body-font-size, var(--fk-typography-body-font-size, .875rem));color:var(--fk-data-table-loading-color, var(--fk-color-text-muted, #6b7a8d))}.fk-data-table__resize-handle{position:absolute;top:0;right:0;bottom:0;width:var(--fk-data-table-resize-handle-width, 4px);cursor:col-resize;background:transparent;z-index:1}.fk-data-table__resize-handle:hover,.fk-data-table__resize-handle:active{background:var(--fk-data-table-resize-handle-color, var(--fk-color-primary, #3b82f6))}.fk-data-table__th--pinned-start,.fk-data-table__td--pinned-start{position:sticky;left:var(--fk-data-table-pin-offset, 0);z-index:2;background:inherit}.fk-data-table__th--pinned-end,.fk-data-table__td--pinned-end{position:sticky;right:var(--fk-data-table-pin-offset, 0);z-index:2;background:inherit}.fk-data-table__selection-cell{flex:none;width:var(--fk-data-table-selection-cell-width, 2.75rem);min-width:var(--fk-data-table-selection-cell-width, 2.75rem);max-width:var(--fk-data-table-selection-cell-width, 2.75rem);text-align:center;cursor:default}.fk-data-table__selection-cell:hover{color:inherit}.fk-data-table__row--selected{background:var(--fk-data-table-row-selected-bg, var(--fk-color-primary-light, #eaf4ff))}.fk-data-table__row--selected:hover{background:var(--fk-data-table-row-selected-hover-bg, var(--fk-color-primary-light, #dbe9ff))}.fk-data-table__expand-cell{flex:none;width:var(--fk-data-table-expand-cell-width, 2.5rem);min-width:var(--fk-data-table-expand-cell-width, 2.5rem);max-width:var(--fk-data-table-expand-cell-width, 2.5rem);text-align:center;cursor:default;padding-inline:var(--fk-rhythm-1, .25rem)}.fk-data-table__expand-button{appearance:none;display:inline-flex;align-items:center;justify-content:center;width:var(--fk-data-table-expand-button-size, 1.5rem);height:var(--fk-data-table-expand-button-size, 1.5rem);padding:0;background:transparent;border:1px solid transparent;border-radius:var(--fk-data-table-expand-button-radius, .25rem);color:var(--fk-data-table-expand-color, var(--fk-color-text-muted, #5d6b80));cursor:pointer;transition:background-color .15s ease,color .15s ease,transform .15s ease}.fk-data-table__expand-button:hover{background:var(--fk-data-table-expand-button-hover-bg, var(--fk-color-surface-muted, #f1f4f8));color:var(--fk-data-table-expand-color-hover, var(--fk-color-text, #1c2433))}.fk-data-table__expand-button:focus-visible{outline:2px solid var(--fk-data-table-expand-focus-ring, var(--fk-color-primary, #0066cc));outline-offset:2px}.fk-data-table__expand-button--expanded{color:var(--fk-data-table-expand-color-active, var(--fk-color-primary, #0066cc))}.fk-data-table__expand-button--expanded .fk-data-table__expand-chevron{transform:rotate(90deg)}.fk-data-table__expand-chevron{transition:transform .15s ease}.fk-data-table__row--expanded{background:var(--fk-data-table-row-expanded-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-data-table__detail-row{background:var(--fk-data-table-detail-row-bg, var(--fk-color-surface, #ffffff))}.fk-data-table__detail-cell{padding:var(--fk-data-table-detail-cell-padding, var(--fk-rhythm-4, 1rem));border-top:1px solid var(--fk-data-table-detail-border-color, var(--fk-color-border, #d9e2ee));border-bottom:1px solid var(--fk-data-table-detail-border-color, var(--fk-color-border, #d9e2ee))}.fk-data-table__checkbox{appearance:none;width:var(--fk-data-table-checkbox-size, 1rem);height:var(--fk-data-table-checkbox-size, 1rem);margin:0;border:1px solid var(--fk-data-table-checkbox-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-data-table-checkbox-radius, .25rem);background:var(--fk-data-table-checkbox-bg, var(--fk-color-surface, #ffffff));cursor:pointer;transition:background-color .15s ease,border-color .15s ease,box-shadow .15s ease;position:relative;vertical-align:middle}.fk-data-table__checkbox:focus-visible{outline:none;box-shadow:var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18))}.fk-data-table__checkbox:hover:not(:disabled){border-color:var(--fk-data-table-checkbox-hover-border-color, var(--fk-color-primary, #0a84ff))}.fk-data-table__checkbox:checked,.fk-data-table__checkbox:indeterminate{background:var(--fk-data-table-checkbox-checked-bg, var(--fk-color-primary, #0a84ff));border-color:var(--fk-data-table-checkbox-checked-border-color, var(--fk-color-primary, #0a84ff))}.fk-data-table__checkbox:checked:after{content:\"\";position:absolute;inset:0;background:var(--fk-data-table-checkbox-icon-color, var(--fk-white, #ffffff));mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='currentColor' d='M6.5 11L3 7.5l1-1L6.5 9 12 3.5l1 1z'/></svg>\") center/75% 75% no-repeat;-webkit-mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='currentColor' d='M6.5 11L3 7.5l1-1L6.5 9 12 3.5l1 1z'/></svg>\") center/75% 75% no-repeat}.fk-data-table__checkbox:indeterminate:after{content:\"\";position:absolute;inset:0;background:var(--fk-data-table-checkbox-icon-color, var(--fk-white, #ffffff));mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><rect x='3' y='7' width='10' height='2' rx='1' fill='currentColor'/></svg>\") center/75% 75% no-repeat;-webkit-mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><rect x='3' y='7' width='10' height='2' rx='1' fill='currentColor'/></svg>\") center/75% 75% no-repeat}.fk-data-table__checkbox:disabled{cursor:not-allowed;opacity:.5}.fk-data-table--resizing{cursor:col-resize;-webkit-user-select:none;user-select:none}.fk-data-table--sticky-header .fk-data-table__head{position:sticky;top:0;z-index:1}.fk-data-table--sticky-header .fk-data-table__table .fk-data-table__th{position:sticky;top:0;z-index:1;background:var(--fk-data-table-head-bg, var(--fk-color-surface-muted, #f7f9fb))}\n"], dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: DataTableStackComponent, selector: "fk-data-table-stack", inputs: ["rows", "columns", "config", "trackByFn", "groupHeadingRenderer", "rowSelection", "selectedKeys", "getRowId", "selectionDisabled", "selectionLabel"], outputs: ["rowClick", "toggleRow"] }, { kind: "directive", type: ColumnResizeDirective, selector: "[fkColumnResize]", inputs: ["fkColumnResize", "enabled", "minWidth", "maxWidth"], outputs: ["columnResize"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1219
1290
  }
1220
1291
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataTableComponent, decorators: [{
1221
1292
  type: Component,
@@ -1228,8 +1299,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
1228
1299
  TableEngineService,
1229
1300
  VirtualizationEngineService,
1230
1301
  ResponsiveStrategyService,
1231
- ], template: "@if (activeMode() === 'stack') {\n <!-- Stack path: mobile card view -->\n <fk-data-table-stack\n [rows]=\"engine.processedRows()\"\n [columns]=\"columns()\"\n [config]=\"mergedConfig()\"\n [trackByFn]=\"mergedConfig().trackBy\"\n [rowSelection]=\"rowSelection()\"\n [selectedKeys]=\"effectiveSelectedKeysForTemplate()\"\n [getRowId]=\"getRowId()\"\n [selectionDisabled]=\"selectionDisabled()\"\n [selectionLabel]=\"selectionLabel()\"\n (rowClick)=\"rowClick.emit($event)\"\n (toggleRow)=\"onToggleRow($event.row, $event.event)\"\n />\n} @else if (useVirtualization()) {\n <!-- Virtual path: div/grid-based with virtual scrolling -->\n <div role=\"table\" [attr.aria-label]=\"ariaLabel()\">\n @if (mergedConfig().showHeader) {\n <div class=\"fk-data-table__head\" role=\"rowgroup\">\n <div class=\"fk-data-table__head-row\" role=\"row\">\n @if (rowSelection() && selectionColumnPosition() === 'start') {\n <div\n class=\"fk-data-table__th fk-data-table__selection-cell\"\n role=\"columnheader\"\n >\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"\n headerSelectionState() === 'checked'\n ? 'Deselect all on page'\n : 'Select all on page'\n \"\n [checked]=\"headerSelectionState() === 'checked'\"\n [indeterminate]=\"headerSelectionState() === 'indeterminate'\"\n (click)=\"onToggleAllVisible($event)\"\n />\n </div>\n }\n @for (column of visibleColumns(); track column.id) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div\n class=\"fk-data-table__th\"\n role=\"columnheader\"\n [fkColumnResize]=\"column.id\"\n [enabled]=\"column.resizable === true\"\n (columnResize)=\"onColumnResize($event)\"\n [class.fk-data-table__th--sortable]=\"column.sortable\"\n [class.fk-data-table__th--align-center]=\"\n column.align === 'center'\n \"\n [class.fk-data-table__th--align-end]=\"column.align === 'end'\"\n [class.fk-data-table__th--pinned-start]=\"\n column.pinned === 'start'\n \"\n [class.fk-data-table__th--pinned-end]=\"column.pinned === 'end'\"\n [style.width]=\"column.width ?? null\"\n [style.min-width]=\"column.minWidth ?? null\"\n [style.max-width]=\"column.maxWidth ?? null\"\n [style.flex]=\"column.flex ?? null\"\n [style.--fk-data-table-pin-offset]=\"getPinOffset(column)\"\n [attr.aria-sort]=\"\n getSortDirection(column.id) === 'asc'\n ? 'ascending'\n : getSortDirection(column.id) === 'desc'\n ? 'descending'\n : null\n \"\n (click)=\"onHeaderClick(column)\"\n >\n @if (column.headerRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.headerRenderer\"\n [ngTemplateOutletContext]=\"{ $implicit: column }\"\n />\n } @else {\n {{ column.header }}\n }\n\n @if (column.sortable) {\n <span\n class=\"fk-data-table__sort-indicator\"\n [class.fk-data-table__sort-indicator--asc]=\"\n getSortDirection(column.id) === 'asc'\n \"\n [class.fk-data-table__sort-indicator--desc]=\"\n getSortDirection(column.id) === 'desc'\n \"\n aria-hidden=\"true\"\n >\n <svg\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 10 10\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path d=\"M5 2L8 6H2L5 2Z\" fill=\"currentColor\" />\n </svg>\n </span>\n }\n </div>\n }\n @if (rowSelection() && selectionColumnPosition() === 'end') {\n <div\n class=\"fk-data-table__th fk-data-table__selection-cell\"\n role=\"columnheader\"\n >\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"\n headerSelectionState() === 'checked'\n ? 'Deselect all on page'\n : 'Select all on page'\n \"\n [checked]=\"headerSelectionState() === 'checked'\"\n [indeterminate]=\"headerSelectionState() === 'indeterminate'\"\n (click)=\"onToggleAllVisible($event)\"\n />\n </div>\n }\n </div>\n </div>\n }\n\n <div\n #virtualViewport\n class=\"fk-data-table__viewport\"\n role=\"rowgroup\"\n (scroll)=\"onVirtualScroll($event)\"\n >\n <div\n class=\"fk-data-table__sentinel\"\n [style.height.px]=\"virtEngine.totalHeight()\"\n ></div>\n\n <div\n class=\"fk-data-table__virtual-body\"\n [style.transform]=\"'translateY(' + virtEngine.offsetY() + 'px)'\"\n >\n @for (\n row of virtualRows();\n track mergedConfig().trackBy(virtualStartIndex() + $index, row);\n let i = $index\n ) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div\n class=\"fk-data-table__row\"\n role=\"row\"\n [class.fk-data-table__row--selected]=\"isRowSelected(row)\"\n [style.height.px]=\"mergedConfig().rowHeight\"\n [ngStyle]=\"getRowStyle(row, virtualStartIndex() + i)\"\n (click)=\"onRowClick(row, virtualStartIndex() + i)\"\n >\n @if (rowSelection() && selectionColumnPosition() === 'start') {\n <div\n class=\"fk-data-table__td fk-data-table__selection-cell\"\n role=\"cell\"\n >\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"selectionLabel()\"\n [checked]=\"isRowSelected(row)\"\n [disabled]=\"isRowSelectionDisabled(row)\"\n (click)=\"onToggleRow(row, $event)\"\n />\n </div>\n }\n @for (column of visibleColumns(); track column.id) {\n <div\n class=\"fk-data-table__td\"\n role=\"cell\"\n [class.fk-data-table__td--truncate]=\"column.truncate\"\n [class.fk-data-table__td--align-center]=\"\n column.align === 'center'\n \"\n [class.fk-data-table__td--align-end]=\"column.align === 'end'\"\n [class.fk-data-table__td--pinned-start]=\"\n column.pinned === 'start'\n \"\n [class.fk-data-table__td--pinned-end]=\"column.pinned === 'end'\"\n [style.width]=\"column.width ?? null\"\n [style.min-width]=\"column.minWidth ?? null\"\n [style.max-width]=\"column.maxWidth ?? null\"\n [style.flex]=\"column.flex ?? null\"\n [style.--fk-data-table-pin-offset]=\"getPinOffset(column)\"\n >\n @if (column.cellRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.cellRenderer\"\n [ngTemplateOutletContext]=\"{\n $implicit: getCellValue(column, row),\n row: row,\n }\"\n />\n } @else {\n {{ getCellValue(column, row) }}\n }\n </div>\n }\n @if (rowSelection() && selectionColumnPosition() === 'end') {\n <div\n class=\"fk-data-table__td fk-data-table__selection-cell\"\n role=\"cell\"\n >\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"selectionLabel()\"\n [checked]=\"isRowSelected(row)\"\n [disabled]=\"isRowSelectionDisabled(row)\"\n (click)=\"onToggleRow(row, $event)\"\n />\n </div>\n }\n </div>\n }\n </div>\n </div>\n\n @if (isEmpty()) {\n <div class=\"fk-data-table__empty\" role=\"row\">\n <div role=\"cell\">{{ mergedConfig().emptyMessage }}</div>\n </div>\n }\n </div>\n} @else {\n <!-- Standard path: semantic table -->\n <div class=\"fk-data-table__scroll\">\n <table class=\"fk-data-table__table\" [attr.aria-label]=\"ariaLabel()\">\n @if (mergedConfig().showHeader) {\n <thead class=\"fk-data-table__head\">\n <tr class=\"fk-data-table__head-row\">\n @if (rowSelection() && selectionColumnPosition() === 'start') {\n <th class=\"fk-data-table__th fk-data-table__selection-cell\">\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"\n headerSelectionState() === 'checked'\n ? 'Deselect all on page'\n : 'Select all on page'\n \"\n [checked]=\"headerSelectionState() === 'checked'\"\n [indeterminate]=\"headerSelectionState() === 'indeterminate'\"\n (click)=\"onToggleAllVisible($event)\"\n />\n </th>\n }\n @if (hasExpansionStart()) {\n <th\n class=\"fk-data-table__th fk-data-table__expand-cell\"\n aria-hidden=\"true\"\n ></th>\n }\n @for (column of visibleColumns(); track column.id) {\n <th\n class=\"fk-data-table__th\"\n [fkColumnResize]=\"column.id\"\n [enabled]=\"column.resizable === true\"\n (columnResize)=\"onColumnResize($event)\"\n [class.fk-data-table__th--sortable]=\"column.sortable\"\n [class.fk-data-table__th--align-center]=\"\n column.align === 'center'\n \"\n [class.fk-data-table__th--align-end]=\"column.align === 'end'\"\n [class.fk-data-table__th--pinned-start]=\"\n column.pinned === 'start'\n \"\n [class.fk-data-table__th--pinned-end]=\"column.pinned === 'end'\"\n [style.width]=\"column.width ?? null\"\n [style.min-width]=\"column.minWidth ?? null\"\n [style.max-width]=\"column.maxWidth ?? null\"\n [style.--fk-data-table-pin-offset]=\"getPinOffset(column)\"\n [attr.aria-sort]=\"\n getSortDirection(column.id) === 'asc'\n ? 'ascending'\n : getSortDirection(column.id) === 'desc'\n ? 'descending'\n : null\n \"\n (click)=\"onHeaderClick(column)\"\n >\n @if (column.headerRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.headerRenderer\"\n [ngTemplateOutletContext]=\"{ $implicit: column }\"\n />\n } @else {\n {{ column.header }}\n }\n\n @if (column.sortable) {\n <span\n class=\"fk-data-table__sort-indicator\"\n [class.fk-data-table__sort-indicator--asc]=\"\n getSortDirection(column.id) === 'asc'\n \"\n [class.fk-data-table__sort-indicator--desc]=\"\n getSortDirection(column.id) === 'desc'\n \"\n aria-hidden=\"true\"\n >\n <svg\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 10 10\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path d=\"M5 2L8 6H2L5 2Z\" fill=\"currentColor\" />\n </svg>\n </span>\n }\n </th>\n }\n @if (rowSelection() && selectionColumnPosition() === 'end') {\n <th class=\"fk-data-table__th fk-data-table__selection-cell\">\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"\n headerSelectionState() === 'checked'\n ? 'Deselect all on page'\n : 'Select all on page'\n \"\n [checked]=\"headerSelectionState() === 'checked'\"\n [indeterminate]=\"headerSelectionState() === 'indeterminate'\"\n (click)=\"onToggleAllVisible($event)\"\n />\n </th>\n }\n @if (hasExpansionEnd()) {\n <th\n class=\"fk-data-table__th fk-data-table__expand-cell\"\n aria-hidden=\"true\"\n ></th>\n }\n </tr>\n </thead>\n }\n\n <tbody class=\"fk-data-table__body\">\n @for (\n row of engine.processedRows();\n track mergedConfig().trackBy($index, row);\n let i = $index\n ) {\n <tr\n class=\"fk-data-table__row\"\n [class.fk-data-table__row--selected]=\"isRowSelected(row)\"\n [class.fk-data-table__row--expanded]=\"\n hasExpansion() && isRowExpanded(row)\n \"\n [ngStyle]=\"getRowStyle(row, i)\"\n (click)=\"onRowClick(row, i)\"\n >\n @if (rowSelection() && selectionColumnPosition() === 'start') {\n <td class=\"fk-data-table__td fk-data-table__selection-cell\">\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"selectionLabel()\"\n [checked]=\"isRowSelected(row)\"\n [disabled]=\"isRowSelectionDisabled(row)\"\n (click)=\"onToggleRow(row, $event)\"\n />\n </td>\n }\n @if (hasExpansionStart()) {\n <td class=\"fk-data-table__td fk-data-table__expand-cell\">\n @if (isRowExpandable(row)) {\n <button\n type=\"button\"\n class=\"fk-data-table__expand-button\"\n [class.fk-data-table__expand-button--expanded]=\"\n isRowExpanded(row)\n \"\n [attr.aria-label]=\"expansionLabel()\"\n [attr.aria-expanded]=\"isRowExpanded(row)\"\n (click)=\"onToggleExpand(row, $event)\"\n >\n <svg\n class=\"fk-data-table__expand-chevron\"\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 10 10\"\n fill=\"none\"\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M3 1.5L6.5 5L3 8.5\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n }\n </td>\n }\n @for (column of visibleColumns(); track column.id) {\n <td\n class=\"fk-data-table__td\"\n [class.fk-data-table__td--truncate]=\"column.truncate\"\n [class.fk-data-table__td--align-center]=\"\n column.align === 'center'\n \"\n [class.fk-data-table__td--align-end]=\"column.align === 'end'\"\n [class.fk-data-table__td--pinned-start]=\"\n column.pinned === 'start'\n \"\n [class.fk-data-table__td--pinned-end]=\"column.pinned === 'end'\"\n [style.--fk-data-table-pin-offset]=\"getPinOffset(column)\"\n >\n @if (column.cellRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.cellRenderer\"\n [ngTemplateOutletContext]=\"{\n $implicit: getCellValue(column, row),\n row: row,\n }\"\n />\n } @else {\n {{ getCellValue(column, row) }}\n }\n </td>\n }\n @if (rowSelection() && selectionColumnPosition() === 'end') {\n <td class=\"fk-data-table__td fk-data-table__selection-cell\">\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"selectionLabel()\"\n [checked]=\"isRowSelected(row)\"\n [disabled]=\"isRowSelectionDisabled(row)\"\n (click)=\"onToggleRow(row, $event)\"\n />\n </td>\n }\n @if (hasExpansionEnd()) {\n <td class=\"fk-data-table__td fk-data-table__expand-cell\">\n @if (isRowExpandable(row)) {\n <button\n type=\"button\"\n class=\"fk-data-table__expand-button\"\n [class.fk-data-table__expand-button--expanded]=\"\n isRowExpanded(row)\n \"\n [attr.aria-label]=\"expansionLabel()\"\n [attr.aria-expanded]=\"isRowExpanded(row)\"\n (click)=\"onToggleExpand(row, $event)\"\n >\n <svg\n class=\"fk-data-table__expand-chevron\"\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 10 10\"\n fill=\"none\"\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M3 1.5L6.5 5L3 8.5\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n }\n </td>\n }\n </tr>\n @if (hasExpansion() && isRowExpanded(row)) {\n <tr class=\"fk-data-table__detail-row\">\n <td\n class=\"fk-data-table__detail-cell\"\n [attr.colspan]=\"totalColumnCount()\"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowDetailTemplate()!\"\n [ngTemplateOutletContext]=\"{\n $implicit: row,\n row: row,\n index: i,\n }\"\n />\n </td>\n </tr>\n }\n }\n </tbody>\n </table>\n\n @if (isEmpty()) {\n <div class=\"fk-data-table__empty\">\n {{ mergedConfig().emptyMessage }}\n </div>\n }\n </div>\n}\n\n@if (loading()) {\n <div class=\"fk-data-table__loading-overlay\" aria-live=\"polite\">\n <span class=\"fk-data-table__loading-text\">Loading\u2026</span>\n </div>\n}\n", styles: [":host{display:block;position:relative;border:var(--fk-data-table-border-width, 1px) solid var(--fk-data-table-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-data-table-border-radius, var(--fk-radius-md, .375rem));overflow:hidden}.fk-data-table__scroll{overflow-x:auto;width:100%;max-height:var(--fk-data-table-viewport-height)}.fk-data-table__table{width:100%;border-collapse:collapse;border-spacing:0}.fk-data-table__head{background:var(--fk-data-table-head-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-data-table__head-row{display:flex}.fk-data-table__table .fk-data-table__head-row{display:table-row}.fk-data-table__th{position:relative;padding:var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem));text-align:start;font-weight:var(--fk-data-table-head-font-weight, var(--fk-font-weight-semibold, 600));font-size:var(--fk-data-table-head-font-size, var(--fk-typography-small-font-size, .875rem));color:var(--fk-data-table-head-color, var(--fk-color-text-muted, #6b7a8d));border-bottom:1px solid var(--fk-data-table-border-color, var(--fk-color-border, #d9e2ee));white-space:nowrap;-webkit-user-select:none;user-select:none;flex:1}.fk-data-table__th--sortable{cursor:pointer}.fk-data-table__th--sortable:hover{color:var(--fk-data-table-head-hover-color, var(--fk-color-text, #1f2d3d))}.fk-data-table__th--align-center{text-align:center}.fk-data-table__th--align-end{text-align:end}.fk-data-table__table .fk-data-table__th{flex:none}.fk-data-table__sort-indicator{display:inline-flex;margin-inline-start:var(--fk-rhythm-1, .25rem);opacity:.3;transition:transform .15s ease,opacity .15s ease}.fk-data-table__sort-indicator--asc{opacity:1;transform:rotate(0)}.fk-data-table__sort-indicator--desc{opacity:1;transform:rotate(180deg)}.fk-data-table__viewport{overflow-y:auto;position:relative;max-height:var(--fk-data-table-viewport-height, 600px)}.fk-data-table__sentinel{position:absolute;top:0;left:0;width:1px;visibility:hidden}.fk-data-table__virtual-body{position:relative;will-change:transform}.fk-data-table__body{background:var(--fk-data-table-body-bg, var(--fk-color-surface, #ffffff))}.fk-data-table__row{display:flex;border-bottom:1px solid var(--fk-data-table-border-color, var(--fk-color-border, #d9e2ee));cursor:pointer}.fk-data-table__row:hover{background:var(--fk-data-table-row-hover-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-data-table__row:last-child{border-bottom:none}.fk-data-table__table .fk-data-table__row{display:table-row}.fk-data-table__td{padding:var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem));font-size:var(--fk-data-table-body-font-size, var(--fk-typography-body-font-size, .875rem));color:var(--fk-data-table-body-color, var(--fk-color-text, #1f2d3d));vertical-align:middle;flex:1}.fk-data-table__td--truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fk-data-table__td--align-center{text-align:center}.fk-data-table__td--align-end{text-align:end}.fk-data-table__table .fk-data-table__td{flex:none}.fk-data-table__empty{padding:var(--fk-data-table-empty-padding, var(--fk-rhythm-8, 2rem));text-align:center;color:var(--fk-data-table-empty-color, var(--fk-color-text-muted, #6b7a8d));font-size:var(--fk-data-table-body-font-size, var(--fk-typography-body-font-size, .875rem))}.fk-data-table__loading-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:var(--fk-data-table-loading-bg, var(--fk-color-surface-overlay, rgba(255, 255, 255, .7)));z-index:1}.fk-data-table__loading-text{font-size:var(--fk-data-table-body-font-size, var(--fk-typography-body-font-size, .875rem));color:var(--fk-data-table-loading-color, var(--fk-color-text-muted, #6b7a8d))}.fk-data-table__resize-handle{position:absolute;top:0;right:0;bottom:0;width:var(--fk-data-table-resize-handle-width, 4px);cursor:col-resize;background:transparent;z-index:1}.fk-data-table__resize-handle:hover,.fk-data-table__resize-handle:active{background:var(--fk-data-table-resize-handle-color, var(--fk-color-primary, #3b82f6))}.fk-data-table__th--pinned-start,.fk-data-table__td--pinned-start{position:sticky;left:var(--fk-data-table-pin-offset, 0);z-index:2;background:inherit}.fk-data-table__th--pinned-end,.fk-data-table__td--pinned-end{position:sticky;right:var(--fk-data-table-pin-offset, 0);z-index:2;background:inherit}.fk-data-table__selection-cell{flex:none;width:var(--fk-data-table-selection-cell-width, 2.75rem);min-width:var(--fk-data-table-selection-cell-width, 2.75rem);max-width:var(--fk-data-table-selection-cell-width, 2.75rem);text-align:center;cursor:default}.fk-data-table__selection-cell:hover{color:inherit}.fk-data-table__row--selected{background:var(--fk-data-table-row-selected-bg, var(--fk-color-primary-light, #eaf4ff))}.fk-data-table__row--selected:hover{background:var(--fk-data-table-row-selected-hover-bg, var(--fk-color-primary-light, #dbe9ff))}.fk-data-table__expand-cell{flex:none;width:var(--fk-data-table-expand-cell-width, 2.5rem);min-width:var(--fk-data-table-expand-cell-width, 2.5rem);max-width:var(--fk-data-table-expand-cell-width, 2.5rem);text-align:center;cursor:default;padding-inline:var(--fk-rhythm-1, .25rem)}.fk-data-table__expand-button{appearance:none;display:inline-flex;align-items:center;justify-content:center;width:var(--fk-data-table-expand-button-size, 1.5rem);height:var(--fk-data-table-expand-button-size, 1.5rem);padding:0;background:transparent;border:1px solid transparent;border-radius:var(--fk-data-table-expand-button-radius, .25rem);color:var(--fk-data-table-expand-color, var(--fk-color-text-muted, #5d6b80));cursor:pointer;transition:background-color .15s ease,color .15s ease,transform .15s ease}.fk-data-table__expand-button:hover{background:var(--fk-data-table-expand-button-hover-bg, var(--fk-color-surface-muted, #f1f4f8));color:var(--fk-data-table-expand-color-hover, var(--fk-color-text, #1c2433))}.fk-data-table__expand-button:focus-visible{outline:2px solid var(--fk-data-table-expand-focus-ring, var(--fk-color-primary, #0066cc));outline-offset:2px}.fk-data-table__expand-button--expanded{color:var(--fk-data-table-expand-color-active, var(--fk-color-primary, #0066cc))}.fk-data-table__expand-button--expanded .fk-data-table__expand-chevron{transform:rotate(90deg)}.fk-data-table__expand-chevron{transition:transform .15s ease}.fk-data-table__row--expanded{background:var(--fk-data-table-row-expanded-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-data-table__detail-row{background:var(--fk-data-table-detail-row-bg, var(--fk-color-surface, #ffffff))}.fk-data-table__detail-cell{padding:var(--fk-data-table-detail-cell-padding, var(--fk-rhythm-4, 1rem));border-top:1px solid var(--fk-data-table-detail-border-color, var(--fk-color-border, #d9e2ee));border-bottom:1px solid var(--fk-data-table-detail-border-color, var(--fk-color-border, #d9e2ee))}.fk-data-table__checkbox{appearance:none;width:var(--fk-data-table-checkbox-size, 1rem);height:var(--fk-data-table-checkbox-size, 1rem);margin:0;border:1px solid var(--fk-data-table-checkbox-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-data-table-checkbox-radius, .25rem);background:var(--fk-data-table-checkbox-bg, var(--fk-color-surface, #ffffff));cursor:pointer;transition:background-color .15s ease,border-color .15s ease,box-shadow .15s ease;position:relative;vertical-align:middle}.fk-data-table__checkbox:focus-visible{outline:none;box-shadow:var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18))}.fk-data-table__checkbox:hover:not(:disabled){border-color:var(--fk-data-table-checkbox-hover-border-color, var(--fk-color-primary, #0a84ff))}.fk-data-table__checkbox:checked,.fk-data-table__checkbox:indeterminate{background:var(--fk-data-table-checkbox-checked-bg, var(--fk-color-primary, #0a84ff));border-color:var(--fk-data-table-checkbox-checked-border-color, var(--fk-color-primary, #0a84ff))}.fk-data-table__checkbox:checked:after{content:\"\";position:absolute;inset:0;background:var(--fk-data-table-checkbox-icon-color, var(--fk-white, #ffffff));mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='currentColor' d='M6.5 11L3 7.5l1-1L6.5 9 12 3.5l1 1z'/></svg>\") center/75% 75% no-repeat;-webkit-mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='currentColor' d='M6.5 11L3 7.5l1-1L6.5 9 12 3.5l1 1z'/></svg>\") center/75% 75% no-repeat}.fk-data-table__checkbox:indeterminate:after{content:\"\";position:absolute;inset:0;background:var(--fk-data-table-checkbox-icon-color, var(--fk-white, #ffffff));mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><rect x='3' y='7' width='10' height='2' rx='1' fill='currentColor'/></svg>\") center/75% 75% no-repeat;-webkit-mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><rect x='3' y='7' width='10' height='2' rx='1' fill='currentColor'/></svg>\") center/75% 75% no-repeat}.fk-data-table__checkbox:disabled{cursor:not-allowed;opacity:.5}.fk-data-table--resizing{cursor:col-resize;-webkit-user-select:none;user-select:none}.fk-data-table--sticky-header .fk-data-table__head{position:sticky;top:0;z-index:1}.fk-data-table--sticky-header .fk-data-table__table .fk-data-table__th{position:sticky;top:0;z-index:1;background:var(--fk-data-table-head-bg, var(--fk-color-surface-muted, #f7f9fb))}\n"] }]
1232
- }], ctorParameters: () => [], propDecorators: { className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], sortState: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortState", required: false }] }], filterState: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterState", required: false }] }], pageState: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageState", required: false }] }], rowSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowSelection", required: false }] }], selectedKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedKeys", required: false }] }], getRowId: [{ type: i0.Input, args: [{ isSignal: true, alias: "getRowId", required: false }] }], selectionDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionDisabled", required: false }] }], selectionColumnPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionColumnPosition", required: false }] }], selectionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionLabel", required: false }] }], rowDetailTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowDetailTemplate", required: false }] }], expansionColumnPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "expansionColumnPosition", required: false }] }], expandedKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedKeys", required: false }] }], expandableRow: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandableRow", required: false }] }], expansionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "expansionLabel", required: false }] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], filterChange: [{ type: i0.Output, args: ["filterChange"] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], expansionChange: [{ type: i0.Output, args: ["expansionChange"] }], columnResize: [{ type: i0.Output, args: ["columnResize"] }], virtualViewport: [{ type: i0.ViewChild, args: ['virtualViewport', { isSignal: true }] }], hostClass: [{
1302
+ ], template: "@if (activeMode() === 'stack') {\n <!-- Stack path: mobile card view -->\n <fk-data-table-stack\n [rows]=\"engine.processedRows()\"\n [columns]=\"columns()\"\n [config]=\"mergedConfig()\"\n [trackByFn]=\"mergedConfig().trackBy\"\n [rowSelection]=\"rowSelection()\"\n [selectedKeys]=\"effectiveSelectedKeysForTemplate()\"\n [getRowId]=\"getRowId()\"\n [selectionDisabled]=\"selectionDisabled()\"\n [selectionLabel]=\"selectionLabel()\"\n [groupHeadingRenderer]=\"groupHeadingRenderer()\"\n (rowClick)=\"rowClick.emit($event)\"\n (toggleRow)=\"onToggleRow($event.row, $event.event)\"\n />\n} @else if (useVirtualization()) {\n <!-- Virtual path: div/grid-based with virtual scrolling -->\n <div role=\"table\" [attr.aria-label]=\"ariaLabel()\">\n @if (mergedConfig().showHeader) {\n <div class=\"fk-data-table__head\" role=\"rowgroup\">\n <div class=\"fk-data-table__head-row\" role=\"row\">\n @if (rowSelection() && selectionColumnPosition() === 'start') {\n <div\n class=\"fk-data-table__th fk-data-table__selection-cell\"\n role=\"columnheader\"\n >\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"\n headerSelectionState() === 'checked'\n ? 'Deselect all on page'\n : 'Select all on page'\n \"\n [checked]=\"headerSelectionState() === 'checked'\"\n [indeterminate]=\"headerSelectionState() === 'indeterminate'\"\n (click)=\"onToggleAllVisible($event)\"\n />\n </div>\n }\n @for (column of visibleColumns(); track column.id) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div\n class=\"fk-data-table__th\"\n role=\"columnheader\"\n [fkColumnResize]=\"column.id\"\n [enabled]=\"column.resizable === true\"\n (columnResize)=\"onColumnResize($event)\"\n [class.fk-data-table__th--sortable]=\"column.sortable\"\n [class.fk-data-table__th--align-center]=\"\n column.align === 'center'\n \"\n [class.fk-data-table__th--align-end]=\"column.align === 'end'\"\n [class.fk-data-table__th--pinned-start]=\"\n column.pinned === 'start'\n \"\n [class.fk-data-table__th--pinned-end]=\"column.pinned === 'end'\"\n [style.width]=\"column.width ?? null\"\n [style.min-width]=\"column.minWidth ?? null\"\n [style.max-width]=\"column.maxWidth ?? null\"\n [style.flex]=\"column.flex ?? null\"\n [style.--fk-data-table-pin-offset]=\"getPinOffset(column)\"\n [attr.aria-sort]=\"\n getSortDirection(column.id) === 'asc'\n ? 'ascending'\n : getSortDirection(column.id) === 'desc'\n ? 'descending'\n : null\n \"\n (click)=\"onHeaderClick(column)\"\n >\n @if (column.headerRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.headerRenderer\"\n [ngTemplateOutletContext]=\"{ $implicit: column }\"\n />\n } @else {\n {{ column.header }}\n }\n\n @if (column.sortable) {\n <span\n class=\"fk-data-table__sort-indicator\"\n [class.fk-data-table__sort-indicator--asc]=\"\n getSortDirection(column.id) === 'asc'\n \"\n [class.fk-data-table__sort-indicator--desc]=\"\n getSortDirection(column.id) === 'desc'\n \"\n aria-hidden=\"true\"\n >\n <svg\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 10 10\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path d=\"M5 2L8 6H2L5 2Z\" fill=\"currentColor\" />\n </svg>\n </span>\n }\n </div>\n }\n @if (rowSelection() && selectionColumnPosition() === 'end') {\n <div\n class=\"fk-data-table__th fk-data-table__selection-cell\"\n role=\"columnheader\"\n >\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"\n headerSelectionState() === 'checked'\n ? 'Deselect all on page'\n : 'Select all on page'\n \"\n [checked]=\"headerSelectionState() === 'checked'\"\n [indeterminate]=\"headerSelectionState() === 'indeterminate'\"\n (click)=\"onToggleAllVisible($event)\"\n />\n </div>\n }\n </div>\n </div>\n }\n\n <div\n #virtualViewport\n class=\"fk-data-table__viewport\"\n role=\"rowgroup\"\n (scroll)=\"onVirtualScroll($event)\"\n >\n <div\n class=\"fk-data-table__sentinel\"\n [style.height.px]=\"virtEngine.totalHeight()\"\n ></div>\n\n <div\n class=\"fk-data-table__virtual-body\"\n [style.transform]=\"'translateY(' + virtEngine.offsetY() + 'px)'\"\n >\n @for (\n row of virtualRows();\n track mergedConfig().trackBy(virtualStartIndex() + $index, row);\n let i = $index\n ) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div\n class=\"fk-data-table__row\"\n role=\"row\"\n [class.fk-data-table__row--selected]=\"isRowSelected(row)\"\n [style.height.px]=\"mergedConfig().rowHeight\"\n [ngStyle]=\"getRowStyle(row, virtualStartIndex() + i)\"\n (click)=\"onRowClick(row, virtualStartIndex() + i)\"\n >\n @if (rowSelection() && selectionColumnPosition() === 'start') {\n <div\n class=\"fk-data-table__td fk-data-table__selection-cell\"\n role=\"cell\"\n >\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"selectionLabel()\"\n [checked]=\"isRowSelected(row)\"\n [disabled]=\"isRowSelectionDisabled(row)\"\n (click)=\"onToggleRow(row, $event)\"\n />\n </div>\n }\n @for (column of visibleColumns(); track column.id) {\n <div\n class=\"fk-data-table__td\"\n role=\"cell\"\n [class.fk-data-table__td--truncate]=\"column.truncate\"\n [class.fk-data-table__td--align-center]=\"\n column.align === 'center'\n \"\n [class.fk-data-table__td--align-end]=\"column.align === 'end'\"\n [class.fk-data-table__td--pinned-start]=\"\n column.pinned === 'start'\n \"\n [class.fk-data-table__td--pinned-end]=\"column.pinned === 'end'\"\n [style.width]=\"column.width ?? null\"\n [style.min-width]=\"column.minWidth ?? null\"\n [style.max-width]=\"column.maxWidth ?? null\"\n [style.flex]=\"column.flex ?? null\"\n [style.--fk-data-table-pin-offset]=\"getPinOffset(column)\"\n >\n @if (column.cellRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.cellRenderer\"\n [ngTemplateOutletContext]=\"{\n $implicit: getCellValue(column, row),\n row: row,\n }\"\n />\n } @else {\n {{ getCellValue(column, row) }}\n }\n </div>\n }\n @if (rowSelection() && selectionColumnPosition() === 'end') {\n <div\n class=\"fk-data-table__td fk-data-table__selection-cell\"\n role=\"cell\"\n >\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"selectionLabel()\"\n [checked]=\"isRowSelected(row)\"\n [disabled]=\"isRowSelectionDisabled(row)\"\n (click)=\"onToggleRow(row, $event)\"\n />\n </div>\n }\n </div>\n }\n </div>\n </div>\n\n @if (isEmpty()) {\n <div class=\"fk-data-table__empty\" role=\"row\">\n <div role=\"cell\">{{ mergedConfig().emptyMessage }}</div>\n </div>\n }\n </div>\n} @else {\n <!-- Standard path: semantic table -->\n <div class=\"fk-data-table__scroll\">\n <table class=\"fk-data-table__table\" [attr.aria-label]=\"ariaLabel()\">\n @if (mergedConfig().showHeader) {\n <thead class=\"fk-data-table__head\">\n <tr class=\"fk-data-table__head-row\">\n @if (rowSelection() && selectionColumnPosition() === 'start') {\n <th class=\"fk-data-table__th fk-data-table__selection-cell\">\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"\n headerSelectionState() === 'checked'\n ? 'Deselect all on page'\n : 'Select all on page'\n \"\n [checked]=\"headerSelectionState() === 'checked'\"\n [indeterminate]=\"headerSelectionState() === 'indeterminate'\"\n (click)=\"onToggleAllVisible($event)\"\n />\n </th>\n }\n @if (hasExpansionStart()) {\n <th\n class=\"fk-data-table__th fk-data-table__expand-cell\"\n aria-hidden=\"true\"\n ></th>\n }\n @for (column of visibleColumns(); track column.id) {\n <th\n class=\"fk-data-table__th\"\n [fkColumnResize]=\"column.id\"\n [enabled]=\"column.resizable === true\"\n (columnResize)=\"onColumnResize($event)\"\n [class.fk-data-table__th--sortable]=\"column.sortable\"\n [class.fk-data-table__th--align-center]=\"\n column.align === 'center'\n \"\n [class.fk-data-table__th--align-end]=\"column.align === 'end'\"\n [class.fk-data-table__th--pinned-start]=\"\n column.pinned === 'start'\n \"\n [class.fk-data-table__th--pinned-end]=\"column.pinned === 'end'\"\n [style.width]=\"column.width ?? null\"\n [style.min-width]=\"column.minWidth ?? null\"\n [style.max-width]=\"column.maxWidth ?? null\"\n [style.--fk-data-table-pin-offset]=\"getPinOffset(column)\"\n [attr.aria-sort]=\"\n getSortDirection(column.id) === 'asc'\n ? 'ascending'\n : getSortDirection(column.id) === 'desc'\n ? 'descending'\n : null\n \"\n (click)=\"onHeaderClick(column)\"\n >\n @if (column.headerRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.headerRenderer\"\n [ngTemplateOutletContext]=\"{ $implicit: column }\"\n />\n } @else {\n {{ column.header }}\n }\n\n @if (column.sortable) {\n <span\n class=\"fk-data-table__sort-indicator\"\n [class.fk-data-table__sort-indicator--asc]=\"\n getSortDirection(column.id) === 'asc'\n \"\n [class.fk-data-table__sort-indicator--desc]=\"\n getSortDirection(column.id) === 'desc'\n \"\n aria-hidden=\"true\"\n >\n <svg\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 10 10\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path d=\"M5 2L8 6H2L5 2Z\" fill=\"currentColor\" />\n </svg>\n </span>\n }\n </th>\n }\n @if (rowSelection() && selectionColumnPosition() === 'end') {\n <th class=\"fk-data-table__th fk-data-table__selection-cell\">\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"\n headerSelectionState() === 'checked'\n ? 'Deselect all on page'\n : 'Select all on page'\n \"\n [checked]=\"headerSelectionState() === 'checked'\"\n [indeterminate]=\"headerSelectionState() === 'indeterminate'\"\n (click)=\"onToggleAllVisible($event)\"\n />\n </th>\n }\n @if (hasExpansionEnd()) {\n <th\n class=\"fk-data-table__th fk-data-table__expand-cell\"\n aria-hidden=\"true\"\n ></th>\n }\n </tr>\n </thead>\n }\n\n <tbody class=\"fk-data-table__body\">\n @for (\n row of engine.processedRows();\n track mergedConfig().trackBy($index, row);\n let i = $index\n ) {\n <!--\n A heading opens each run of rows sharing a group key. Emitted from\n inside the row loop rather than by pre-grouping the rows, so the\n table still renders exactly the rows it was given, in the order it\n was given them.\n -->\n @if (groupHeadingFor(row, i); as heading) {\n <tr class=\"fk-data-table__group-heading\">\n <th\n scope=\"colgroup\"\n class=\"fk-data-table__group-heading-cell\"\n [attr.colspan]=\"groupHeadingSpan()\"\n >\n @if (groupHeadingRenderer()) {\n <ng-container\n [ngTemplateOutlet]=\"groupHeadingRenderer()!\"\n [ngTemplateOutletContext]=\"{ $implicit: heading, row: row }\"\n />\n } @else {\n {{ heading }}\n }\n </th>\n </tr>\n }\n\n <tr\n class=\"fk-data-table__row\"\n [class.fk-data-table__row--selected]=\"isRowSelected(row)\"\n [class.fk-data-table__row--expanded]=\"\n hasExpansion() && isRowExpanded(row)\n \"\n [ngStyle]=\"getRowStyle(row, i)\"\n (click)=\"onRowClick(row, i)\"\n >\n @if (rowSelection() && selectionColumnPosition() === 'start') {\n <td class=\"fk-data-table__td fk-data-table__selection-cell\">\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"selectionLabel()\"\n [checked]=\"isRowSelected(row)\"\n [disabled]=\"isRowSelectionDisabled(row)\"\n (click)=\"onToggleRow(row, $event)\"\n />\n </td>\n }\n @if (hasExpansionStart()) {\n <td class=\"fk-data-table__td fk-data-table__expand-cell\">\n @if (isRowExpandable(row)) {\n <button\n type=\"button\"\n class=\"fk-data-table__expand-button\"\n [class.fk-data-table__expand-button--expanded]=\"\n isRowExpanded(row)\n \"\n [attr.aria-label]=\"expansionLabel()\"\n [attr.aria-expanded]=\"isRowExpanded(row)\"\n (click)=\"onToggleExpand(row, $event)\"\n >\n <svg\n class=\"fk-data-table__expand-chevron\"\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 10 10\"\n fill=\"none\"\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M3 1.5L6.5 5L3 8.5\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n }\n </td>\n }\n @for (column of visibleColumns(); track column.id) {\n <td\n class=\"fk-data-table__td\"\n [class.fk-data-table__td--truncate]=\"column.truncate\"\n [class.fk-data-table__td--align-center]=\"\n column.align === 'center'\n \"\n [class.fk-data-table__td--align-end]=\"column.align === 'end'\"\n [class.fk-data-table__td--pinned-start]=\"\n column.pinned === 'start'\n \"\n [class.fk-data-table__td--pinned-end]=\"column.pinned === 'end'\"\n [style.--fk-data-table-pin-offset]=\"getPinOffset(column)\"\n >\n @if (column.cellRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.cellRenderer\"\n [ngTemplateOutletContext]=\"{\n $implicit: getCellValue(column, row),\n row: row,\n }\"\n />\n } @else {\n {{ getCellValue(column, row) }}\n }\n </td>\n }\n @if (rowSelection() && selectionColumnPosition() === 'end') {\n <td class=\"fk-data-table__td fk-data-table__selection-cell\">\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"selectionLabel()\"\n [checked]=\"isRowSelected(row)\"\n [disabled]=\"isRowSelectionDisabled(row)\"\n (click)=\"onToggleRow(row, $event)\"\n />\n </td>\n }\n @if (hasExpansionEnd()) {\n <td class=\"fk-data-table__td fk-data-table__expand-cell\">\n @if (isRowExpandable(row)) {\n <button\n type=\"button\"\n class=\"fk-data-table__expand-button\"\n [class.fk-data-table__expand-button--expanded]=\"\n isRowExpanded(row)\n \"\n [attr.aria-label]=\"expansionLabel()\"\n [attr.aria-expanded]=\"isRowExpanded(row)\"\n (click)=\"onToggleExpand(row, $event)\"\n >\n <svg\n class=\"fk-data-table__expand-chevron\"\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 10 10\"\n fill=\"none\"\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M3 1.5L6.5 5L3 8.5\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n }\n </td>\n }\n </tr>\n @if (hasExpansion() && isRowExpanded(row)) {\n <tr class=\"fk-data-table__detail-row\">\n <td\n class=\"fk-data-table__detail-cell\"\n [attr.colspan]=\"totalColumnCount()\"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowDetailTemplate()!\"\n [ngTemplateOutletContext]=\"{\n $implicit: row,\n row: row,\n index: i,\n }\"\n />\n </td>\n </tr>\n }\n }\n </tbody>\n </table>\n\n @if (isEmpty()) {\n <div class=\"fk-data-table__empty\">\n {{ mergedConfig().emptyMessage }}\n </div>\n }\n </div>\n}\n\n@if (loading()) {\n <div class=\"fk-data-table__loading-overlay\" aria-live=\"polite\">\n <span class=\"fk-data-table__loading-text\">Loading\u2026</span>\n </div>\n}\n", styles: [":host{display:block;position:relative;border:var(--fk-data-table-border-width, 1px) solid var(--fk-data-table-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-data-table-border-radius, var(--fk-radius-md, .375rem));overflow:hidden}.fk-data-table__scroll{overflow-x:auto;width:100%;max-height:var(--fk-data-table-viewport-height)}.fk-data-table__table{width:100%;border-collapse:collapse;border-spacing:0}.fk-data-table__head{background:var(--fk-data-table-head-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-data-table__head-row{display:flex}.fk-data-table__table .fk-data-table__head-row{display:table-row}.fk-data-table__th{position:relative;padding:var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem));text-align:start;font-weight:var(--fk-data-table-head-font-weight, var(--fk-font-weight-semibold, 600));font-size:var(--fk-data-table-head-font-size, var(--fk-typography-small-font-size, .875rem));color:var(--fk-data-table-head-color, var(--fk-color-text-muted, #6b7a8d));border-bottom:1px solid var(--fk-data-table-border-color, var(--fk-color-border, #d9e2ee));white-space:nowrap;-webkit-user-select:none;user-select:none;flex:1}.fk-data-table__th--sortable{cursor:pointer}.fk-data-table__th--sortable:hover{color:var(--fk-data-table-head-hover-color, var(--fk-color-text, #1f2d3d))}.fk-data-table__th--align-center{text-align:center}.fk-data-table__th--align-end{text-align:end}.fk-data-table__table .fk-data-table__th{flex:none}.fk-data-table__sort-indicator{display:inline-flex;margin-inline-start:var(--fk-rhythm-1, .25rem);opacity:.3;transition:transform .15s ease,opacity .15s ease}.fk-data-table__sort-indicator--asc{opacity:1;transform:rotate(0)}.fk-data-table__sort-indicator--desc{opacity:1;transform:rotate(180deg)}.fk-data-table__viewport{overflow-y:auto;position:relative;max-height:var(--fk-data-table-viewport-height, 600px)}.fk-data-table__sentinel{position:absolute;top:0;left:0;width:1px;visibility:hidden}.fk-data-table__virtual-body{position:relative;will-change:transform}.fk-data-table__group-heading{background:var(--fk-data-table-group-heading-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-data-table__group-heading-cell{padding:var(--fk-data-table-group-heading-padding, var(--fk-rhythm-2, .5rem) var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem)));text-align:start;font-size:var(--fk-data-table-group-heading-font-size, var(--fk-typography-caption-font-size, .75rem));font-weight:var(--fk-data-table-group-heading-font-weight, var(--fk-font-weight-semibold, 600));letter-spacing:var(--fk-data-table-group-heading-letter-spacing, .04em);text-transform:var(--fk-data-table-group-heading-text-transform, uppercase);color:var(--fk-data-table-group-heading-color, var(--fk-color-text-muted, #6b7a8d));border-block:1px solid var(--fk-data-table-border-color, var(--fk-color-border, #d9e2ee))}.fk-data-table__body{background:var(--fk-data-table-body-bg, var(--fk-color-surface, #ffffff))}.fk-data-table__row{display:flex;border-bottom:1px solid var(--fk-data-table-border-color, var(--fk-color-border, #d9e2ee));cursor:pointer}.fk-data-table__row:hover{background:var(--fk-data-table-row-hover-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-data-table__row:last-child{border-bottom:none}.fk-data-table__table .fk-data-table__row{display:table-row}.fk-data-table__td{padding:var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem));font-size:var(--fk-data-table-body-font-size, var(--fk-typography-body-font-size, .875rem));color:var(--fk-data-table-body-color, var(--fk-color-text, #1f2d3d));vertical-align:middle;flex:1}.fk-data-table__td--truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fk-data-table__td--align-center{text-align:center}.fk-data-table__td--align-end{text-align:end}.fk-data-table__table .fk-data-table__td{flex:none}.fk-data-table__empty{padding:var(--fk-data-table-empty-padding, var(--fk-rhythm-8, 2rem));text-align:center;color:var(--fk-data-table-empty-color, var(--fk-color-text-muted, #6b7a8d));font-size:var(--fk-data-table-body-font-size, var(--fk-typography-body-font-size, .875rem))}.fk-data-table__loading-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:var(--fk-data-table-loading-bg, var(--fk-color-surface-overlay, rgba(255, 255, 255, .7)));z-index:1}.fk-data-table__loading-text{font-size:var(--fk-data-table-body-font-size, var(--fk-typography-body-font-size, .875rem));color:var(--fk-data-table-loading-color, var(--fk-color-text-muted, #6b7a8d))}.fk-data-table__resize-handle{position:absolute;top:0;right:0;bottom:0;width:var(--fk-data-table-resize-handle-width, 4px);cursor:col-resize;background:transparent;z-index:1}.fk-data-table__resize-handle:hover,.fk-data-table__resize-handle:active{background:var(--fk-data-table-resize-handle-color, var(--fk-color-primary, #3b82f6))}.fk-data-table__th--pinned-start,.fk-data-table__td--pinned-start{position:sticky;left:var(--fk-data-table-pin-offset, 0);z-index:2;background:inherit}.fk-data-table__th--pinned-end,.fk-data-table__td--pinned-end{position:sticky;right:var(--fk-data-table-pin-offset, 0);z-index:2;background:inherit}.fk-data-table__selection-cell{flex:none;width:var(--fk-data-table-selection-cell-width, 2.75rem);min-width:var(--fk-data-table-selection-cell-width, 2.75rem);max-width:var(--fk-data-table-selection-cell-width, 2.75rem);text-align:center;cursor:default}.fk-data-table__selection-cell:hover{color:inherit}.fk-data-table__row--selected{background:var(--fk-data-table-row-selected-bg, var(--fk-color-primary-light, #eaf4ff))}.fk-data-table__row--selected:hover{background:var(--fk-data-table-row-selected-hover-bg, var(--fk-color-primary-light, #dbe9ff))}.fk-data-table__expand-cell{flex:none;width:var(--fk-data-table-expand-cell-width, 2.5rem);min-width:var(--fk-data-table-expand-cell-width, 2.5rem);max-width:var(--fk-data-table-expand-cell-width, 2.5rem);text-align:center;cursor:default;padding-inline:var(--fk-rhythm-1, .25rem)}.fk-data-table__expand-button{appearance:none;display:inline-flex;align-items:center;justify-content:center;width:var(--fk-data-table-expand-button-size, 1.5rem);height:var(--fk-data-table-expand-button-size, 1.5rem);padding:0;background:transparent;border:1px solid transparent;border-radius:var(--fk-data-table-expand-button-radius, .25rem);color:var(--fk-data-table-expand-color, var(--fk-color-text-muted, #5d6b80));cursor:pointer;transition:background-color .15s ease,color .15s ease,transform .15s ease}.fk-data-table__expand-button:hover{background:var(--fk-data-table-expand-button-hover-bg, var(--fk-color-surface-muted, #f1f4f8));color:var(--fk-data-table-expand-color-hover, var(--fk-color-text, #1c2433))}.fk-data-table__expand-button:focus-visible{outline:2px solid var(--fk-data-table-expand-focus-ring, var(--fk-color-primary, #0066cc));outline-offset:2px}.fk-data-table__expand-button--expanded{color:var(--fk-data-table-expand-color-active, var(--fk-color-primary, #0066cc))}.fk-data-table__expand-button--expanded .fk-data-table__expand-chevron{transform:rotate(90deg)}.fk-data-table__expand-chevron{transition:transform .15s ease}.fk-data-table__row--expanded{background:var(--fk-data-table-row-expanded-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-data-table__detail-row{background:var(--fk-data-table-detail-row-bg, var(--fk-color-surface, #ffffff))}.fk-data-table__detail-cell{padding:var(--fk-data-table-detail-cell-padding, var(--fk-rhythm-4, 1rem));border-top:1px solid var(--fk-data-table-detail-border-color, var(--fk-color-border, #d9e2ee));border-bottom:1px solid var(--fk-data-table-detail-border-color, var(--fk-color-border, #d9e2ee))}.fk-data-table__checkbox{appearance:none;width:var(--fk-data-table-checkbox-size, 1rem);height:var(--fk-data-table-checkbox-size, 1rem);margin:0;border:1px solid var(--fk-data-table-checkbox-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-data-table-checkbox-radius, .25rem);background:var(--fk-data-table-checkbox-bg, var(--fk-color-surface, #ffffff));cursor:pointer;transition:background-color .15s ease,border-color .15s ease,box-shadow .15s ease;position:relative;vertical-align:middle}.fk-data-table__checkbox:focus-visible{outline:none;box-shadow:var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18))}.fk-data-table__checkbox:hover:not(:disabled){border-color:var(--fk-data-table-checkbox-hover-border-color, var(--fk-color-primary, #0a84ff))}.fk-data-table__checkbox:checked,.fk-data-table__checkbox:indeterminate{background:var(--fk-data-table-checkbox-checked-bg, var(--fk-color-primary, #0a84ff));border-color:var(--fk-data-table-checkbox-checked-border-color, var(--fk-color-primary, #0a84ff))}.fk-data-table__checkbox:checked:after{content:\"\";position:absolute;inset:0;background:var(--fk-data-table-checkbox-icon-color, var(--fk-white, #ffffff));mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='currentColor' d='M6.5 11L3 7.5l1-1L6.5 9 12 3.5l1 1z'/></svg>\") center/75% 75% no-repeat;-webkit-mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='currentColor' d='M6.5 11L3 7.5l1-1L6.5 9 12 3.5l1 1z'/></svg>\") center/75% 75% no-repeat}.fk-data-table__checkbox:indeterminate:after{content:\"\";position:absolute;inset:0;background:var(--fk-data-table-checkbox-icon-color, var(--fk-white, #ffffff));mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><rect x='3' y='7' width='10' height='2' rx='1' fill='currentColor'/></svg>\") center/75% 75% no-repeat;-webkit-mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><rect x='3' y='7' width='10' height='2' rx='1' fill='currentColor'/></svg>\") center/75% 75% no-repeat}.fk-data-table__checkbox:disabled{cursor:not-allowed;opacity:.5}.fk-data-table--resizing{cursor:col-resize;-webkit-user-select:none;user-select:none}.fk-data-table--sticky-header .fk-data-table__head{position:sticky;top:0;z-index:1}.fk-data-table--sticky-header .fk-data-table__table .fk-data-table__th{position:sticky;top:0;z-index:1;background:var(--fk-data-table-head-bg, var(--fk-color-surface-muted, #f7f9fb))}\n"] }]
1303
+ }], ctorParameters: () => [], propDecorators: { className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], sortState: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortState", required: false }] }], filterState: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterState", required: false }] }], pageState: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageState", required: false }] }], rowSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowSelection", required: false }] }], selectedKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedKeys", required: false }] }], getRowId: [{ type: i0.Input, args: [{ isSignal: true, alias: "getRowId", required: false }] }], selectionDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionDisabled", required: false }] }], selectionColumnPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionColumnPosition", required: false }] }], selectionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionLabel", required: false }] }], rowDetailTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowDetailTemplate", required: false }] }], expansionColumnPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "expansionColumnPosition", required: false }] }], expandedKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedKeys", required: false }] }], expandableRow: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandableRow", required: false }] }], expansionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "expansionLabel", required: false }] }], groupHeadingRenderer: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupHeadingRenderer", required: false }] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], filterChange: [{ type: i0.Output, args: ["filterChange"] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], expansionChange: [{ type: i0.Output, args: ["expansionChange"] }], columnResize: [{ type: i0.Output, args: ["columnResize"] }], virtualViewport: [{ type: i0.ViewChild, args: ['virtualViewport', { isSignal: true }] }], hostClass: [{
1233
1304
  type: HostBinding,
1234
1305
  args: ['class']
1235
1306
  }], hostId: [{