@frame-kit/ui-ng 0.2.3 → 0.2.5

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',
@@ -748,6 +749,13 @@ class DataTableComponent {
748
749
  expandableRow = input(null, ...(ngDevMode ? [{ debugName: "expandableRow" }] : /* istanbul ignore next */ []));
749
750
  /** Accessible label for each row's expansion chevron. */
750
751
  expansionLabel = input('Toggle row details', ...(ngDevMode ? [{ debugName: "expansionLabel" }] : /* istanbul ignore next */ []));
752
+ // ===== GROUPING =====
753
+ /**
754
+ * Template rendered inside a group heading. Receives the group key and the
755
+ * first row of the group, so a heading can show more than the key it was
756
+ * grouped by. Omit it and the key is rendered as text.
757
+ */
758
+ groupHeadingRenderer = input(null, ...(ngDevMode ? [{ debugName: "groupHeadingRenderer" }] : /* istanbul ignore next */ []));
751
759
  // ===== OUTPUTS =====
752
760
  /** Fires when the user clicks a sortable column header, emitting the new sort state. */
753
761
  sortChange = output();
@@ -783,6 +791,13 @@ class DataTableComponent {
783
791
  useVirtualization = computed(() => {
784
792
  const config = this.mergedConfig();
785
793
  const rowCount = this.rows().length;
794
+ // Virtualization positions rows by multiplying an index by `rowHeight`.
795
+ // A group heading is not that tall, so every row after the first heading
796
+ // would be placed wrong. Grouping wins; the alternative is a table that
797
+ // silently misplaces its own rows.
798
+ if (config.groupBy) {
799
+ return false;
800
+ }
786
801
  if (config.virtualization === true) {
787
802
  return true;
788
803
  }
@@ -799,6 +814,20 @@ class DataTableComponent {
799
814
  virtualStartIndex = computed(() => {
800
815
  return this.virtEngine.visibleRange().start;
801
816
  }, ...(ngDevMode ? [{ debugName: "virtualStartIndex" }] : /* istanbul ignore next */ []));
817
+ /**
818
+ * How many cells a heading has to span — the visible columns plus whichever
819
+ * of the selection and expansion cells the table is currently rendering.
820
+ */
821
+ groupHeadingSpan = computed(() => {
822
+ let span = this.visibleColumns().length;
823
+ if (this.rowSelection()) {
824
+ span += 1;
825
+ }
826
+ if (this.hasExpansion()) {
827
+ span += 1;
828
+ }
829
+ return span;
830
+ }, ...(ngDevMode ? [{ debugName: "groupHeadingSpan" }] : /* istanbul ignore next */ []));
802
831
  isEmpty = computed(() => {
803
832
  return this.engine.processedRows().length === 0 && !this.loading();
804
833
  }, ...(ngDevMode ? [{ debugName: "isEmpty" }] : /* istanbul ignore next */ []));
@@ -894,6 +923,25 @@ class DataTableComponent {
894
923
  });
895
924
  }
896
925
  // ===== TEMPLATE HELPERS =====
926
+ /**
927
+ * The group heading to emit before a row, or null when the row continues the
928
+ * group above it. The first row always opens its group.
929
+ */
930
+ groupHeadingFor(row, index) {
931
+ const groupBy = this.mergedConfig().groupBy;
932
+ if (!groupBy) {
933
+ return null;
934
+ }
935
+ const key = groupBy(row);
936
+ if (key === null) {
937
+ return null;
938
+ }
939
+ if (index === 0) {
940
+ return key;
941
+ }
942
+ const previous = this.engine.processedRows()[index - 1];
943
+ return previous !== undefined && groupBy(previous) === key ? null : key;
944
+ }
897
945
  /** Resolves the display value for a cell given its column definition and row data. */
898
946
  getCellValue(column, row) {
899
947
  if (typeof column.accessor === 'function') {
@@ -1211,11 +1259,11 @@ class DataTableComponent {
1211
1259
  return sort.direction;
1212
1260
  }
1213
1261
  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: [
1262
+ 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
1263
  TableEngineService,
1216
1264
  VirtualizationEngineService,
1217
1265
  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 });
1266
+ ], 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 <!--\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:var(--fk-data-table-border-width, 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", "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
1267
  }
1220
1268
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataTableComponent, decorators: [{
1221
1269
  type: Component,
@@ -1228,8 +1276,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
1228
1276
  TableEngineService,
1229
1277
  VirtualizationEngineService,
1230
1278
  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: [{
1279
+ ], 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 <!--\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:var(--fk-data-table-border-width, 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"] }]
1280
+ }], 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
1281
  type: HostBinding,
1234
1282
  args: ['class']
1235
1283
  }], hostId: [{