eru-grid 0.0.18 → 0.0.20

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.
@@ -1,6 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { Injectable, inject, signal, computed, DOCUMENT, effect, Directive, InjectionToken, forwardRef, Input, Component, ElementRef, input, ChangeDetectorRef, model, Renderer2, ViewChild, ViewEncapsulation, ChangeDetectionStrategy, HostListener } from '@angular/core';
3
- import { of, delay } from 'rxjs';
4
3
  import * as i2$1 from '@angular/common';
5
4
  import { NgFor, NgIf, NgClass, DatePipe, CommonModule } from '@angular/common';
6
5
  import { CdkListbox, CdkOption } from '@angular/cdk/listbox';
@@ -101,53 +100,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImpor
101
100
  }]
102
101
  }] });
103
102
 
104
- /**
105
- * Type definitions for Excel export functionality
106
- */
107
- /**
108
- * Type guard to check if a property is a cell reference (not a special Excel property)
109
- */
110
- function isCellReference(key) {
111
- return !key.startsWith('!');
112
- }
113
- /**
114
- * Type guard to check if a property is a special Excel property
115
- */
116
- function isExcelSpecialProperty(key) {
117
- return key.startsWith('!');
118
- }
119
- /**
120
- * Color palette for Excel export styling
121
- */
122
- const EXCEL_COLORS = {
123
- // Header colors (blue gradient)
124
- HEADER_PRIMARY: '4472C4',
125
- HEADER_SECONDARY: '5B9BD5',
126
- HEADER_TERTIARY: 'A5A5A5',
127
- HEADER_QUATERNARY: '70AD47',
128
- // Subtotal colors
129
- SUBTOTAL_HEADER: 'FFD700', // Gold
130
- SUBTOTAL_ROW: 'F0F8FF', // Light blue
131
- GRAND_TOTAL_ROW: 'E6E6FA', // Light purple
132
- // Border colors
133
- BORDER_DARK: '000000', // Black
134
- BORDER_LIGHT: 'CCCCCC', // Light gray
135
- // Text colors
136
- TEXT_WHITE: 'FFFFFF',
137
- TEXT_BLACK: '000000',
138
- TEXT_DARK: '333333'
139
- };
140
- /**
141
- * Default Excel export configuration
142
- */
143
- const DEFAULT_EXCEL_CONFIG = {
144
- filename: 'pivot-table.xlsx',
145
- sheetName: 'Pivot Table',
146
- includeHeaders: true,
147
- includeStyling: true,
148
- includeMerges: true
149
- };
150
-
151
103
  class PivotTransformService {
152
104
  columnGroupState = {};
153
105
  /**
@@ -1848,352 +1800,12 @@ class PivotTransformService {
1848
1800
  }
1849
1801
  return groups;
1850
1802
  }
1851
- /**
1852
- * Export pivot data to CSV format
1853
- */
1854
- exportToCsv(pivotResult) {
1855
- const headers = pivotResult.columnDefinitions.map(col => col.label).join(',');
1856
- const rows = pivotResult.data.map(row => {
1857
- return pivotResult.columnDefinitions.map(col => {
1858
- const value = row[col.name];
1859
- return typeof value === 'string' && value.includes(',')
1860
- ? `"${value}"`
1861
- : value || '';
1862
- }).join(',');
1863
- });
1864
- return [headers, ...rows].join('\n');
1865
- }
1866
- /**
1867
- * Export pivot data to Excel format with proper formatting, cell merging, and styling
1868
- * Returns a workbook object compatible with xlsx library
1869
- */
1870
- exportToExcel(pivotResult, options) {
1871
- const { data, columnDefinitions, headerStructure, configuration } = pivotResult;
1872
- const exportOptions = { ...DEFAULT_EXCEL_CONFIG, ...options };
1873
- if (!headerStructure) {
1874
- throw new Error('Header structure is required for Excel export');
1875
- }
1876
- // Use simplified approach that works reliably
1877
- const sheet = this.createSimplifiedXlsxSheet(data, columnDefinitions, headerStructure, configuration, exportOptions);
1878
- const workbook = {
1879
- SheetNames: [exportOptions.sheetName],
1880
- Sheets: {
1881
- [exportOptions.sheetName]: sheet
1882
- }
1883
- };
1884
- return workbook;
1885
- }
1886
- /**
1887
- * Export pivot data to Excel file using xlsx library
1888
- * This method requires the xlsx library to be installed: npm install xlsx
1889
- * Usage:
1890
- * import * as XLSX from 'xlsx';
1891
- * const workbook = pivotService.exportToExcel(pivotResult);
1892
- * XLSX.writeFile(workbook, 'pivot-table.xlsx');
1893
- */
1894
- exportToExcelFile(pivotResult, filename = 'pivot-table.xlsx') {
1895
- const workbook = this.exportToExcel(pivotResult);
1896
- // This requires xlsx library
1897
- // import * as XLSX from 'xlsx';
1898
- // XLSX.writeFile(workbook, filename);
1899
- console.log('Excel workbook created. Use XLSX.writeFile(workbook, filename) to save the file.');
1900
- console.log('Workbook structure:', workbook);
1901
- }
1902
- /**
1903
- * Create xlsx-compatible sheet with proper formatting and cell merging
1904
- */
1905
- createXlsxSheet(data, columnDefinitions, headerStructure, rowspanInfo, configuration, options) {
1906
- const sheet = {};
1907
- const merges = [];
1908
- // Start row for data (after headers)
1909
- let currentRow = headerStructure.maxDepth + 1;
1910
- // Add header rows with proper formatting
1911
- if (options.includeHeaders) {
1912
- this.addXlsxHeaders(sheet, headerStructure, merges, options);
1913
- }
1914
- // Add data rows with rowspan handling
1915
- this.addXlsxDataRows(sheet, data, columnDefinitions, rowspanInfo, currentRow, merges, options);
1916
- // Set column widths
1917
- this.setXlsxColumnWidths(sheet, columnDefinitions);
1918
- // Set merge ranges - only if we have valid merges
1919
- if (options.includeMerges && merges.length > 0) {
1920
- // Filter out invalid merges
1921
- const validMerges = merges.filter(merge => merge.s && merge.e &&
1922
- merge.s.r >= 0 && merge.s.c >= 0 &&
1923
- merge.e.r >= 0 && merge.e.c >= 0 &&
1924
- merge.s.r <= merge.e.r && merge.s.c <= merge.e.c);
1925
- if (validMerges.length > 0) {
1926
- sheet['!merges'] = validMerges;
1927
- }
1928
- }
1929
- // Set sheet range
1930
- const range = this.getXlsxRange(sheet);
1931
- if (range) {
1932
- sheet['!ref'] = range;
1933
- }
1934
- return sheet;
1935
- }
1936
- /**
1937
- * Create Excel sheet with proper formatting and cell merging (legacy method)
1938
- */
1939
- createExcelSheet(data, columnDefinitions, headerStructure, rowspanInfo, configuration, options) {
1940
- const sheet = {};
1941
- // Define cell ranges for merging
1942
- const merges = [];
1943
- // Start row for data (after headers)
1944
- let currentRow = headerStructure.maxDepth + 1;
1945
- // Add header rows with proper formatting
1946
- if (options.includeHeaders) {
1947
- this.addExcelHeaders(sheet, headerStructure, merges, options);
1948
- }
1949
- // Add data rows with rowspan handling
1950
- this.addExcelDataRows(sheet, data, columnDefinitions, rowspanInfo, currentRow, merges, options);
1951
- // Set column widths
1952
- this.setExcelColumnWidths(sheet, columnDefinitions);
1953
- // Apply styling
1954
- if (options.includeStyling) {
1955
- this.applyExcelStyling(sheet, headerStructure, data, configuration);
1956
- }
1957
- // Set merge ranges
1958
- if (options.includeMerges) {
1959
- sheet['!merges'] = merges.map(range => {
1960
- const [startCell, endCell] = range.split(':');
1961
- const startRef = this.parseExcelCellRef(startCell);
1962
- const endRef = this.parseExcelCellRef(endCell);
1963
- return {
1964
- s: { r: startRef.row, c: startRef.col },
1965
- e: { r: endRef.row, c: endRef.col }
1966
- };
1967
- });
1968
- }
1969
- return sheet;
1970
- }
1971
- /**
1972
- * Add Excel header rows with proper formatting and merging
1973
- */
1974
- addExcelHeaders(sheet, headerStructure, merges, options) {
1975
- const { headerRows, maxDepth } = headerStructure;
1976
- headerRows.forEach((headerRow, rowIndex) => {
1977
- headerRow.forEach((header, colIndex) => {
1978
- const cellRef = this.getExcelCellRef(rowIndex, colIndex);
1979
- sheet[cellRef] = {
1980
- v: header.label,
1981
- t: 's',
1982
- s: this.getHeaderCellStyle(header, rowIndex, maxDepth)
1983
- };
1984
- // Add merge ranges for headers with colspan > 1
1985
- if (header.colspan > 1) {
1986
- const endCol = colIndex + header.colspan - 1;
1987
- const mergeRange = `${cellRef}:${this.getExcelCellRef(rowIndex, endCol)}`;
1988
- merges.push(mergeRange);
1989
- }
1990
- // Add merge ranges for headers with rowspan > 1
1991
- if (header.rowspan > 1) {
1992
- const endRow = rowIndex + header.rowspan - 1;
1993
- const mergeRange = `${cellRef}:${this.getExcelCellRef(endRow, colIndex)}`;
1994
- merges.push(mergeRange);
1995
- }
1996
- });
1997
- });
1998
- }
1999
- /**
2000
- * Add Excel data rows with rowspan handling
2001
- */
2002
- addExcelDataRows(sheet, data, columnDefinitions, rowspanInfo, startRow, merges, options) {
2003
- data.forEach((row, rowIndex) => {
2004
- const excelRowIndex = startRow + rowIndex;
2005
- columnDefinitions.forEach((col, colIndex) => {
2006
- const cellRef = this.getExcelCellRef(excelRowIndex, colIndex);
2007
- const value = row[col.name];
2008
- // Check if this cell should be merged (rowspan)
2009
- const rowspanKey = `${rowIndex}_${col.name}`;
2010
- const spanInfo = rowspanInfo.get(rowspanKey);
2011
- if (spanInfo && spanInfo.skip) {
2012
- // Skip this cell as it's merged with a previous cell
2013
- return;
2014
- }
2015
- // Set cell value and style
2016
- sheet[cellRef] = {
2017
- v: this.formatExcelValue(value, col.datatype),
2018
- t: this.getExcelValueType(value, col.datatype),
2019
- s: this.getDataCellStyle(row, col, spanInfo)
2020
- };
2021
- // Add merge range if this cell has rowspan > 1
2022
- if (spanInfo && spanInfo.rowspan > 1) {
2023
- const endRow = excelRowIndex + spanInfo.rowspan - 1;
2024
- const mergeRange = `${cellRef}:${this.getExcelCellRef(endRow, colIndex)}`;
2025
- merges.push(mergeRange);
2026
- }
2027
- });
2028
- });
2029
- }
2030
- /**
2031
- * Get Excel cell reference (e.g., A1, B2, etc.)
2032
- */
2033
- getExcelCellRef(row, col) {
2034
- const colLetter = this.getExcelColumnLetter(col);
2035
- return `${colLetter}${row + 1}`;
2036
- }
2037
- /**
2038
- * Convert column index to Excel column letter
2039
- */
2040
- getExcelColumnLetter(col) {
2041
- let result = '';
2042
- while (col >= 0) {
2043
- result = String.fromCharCode(65 + (col % 26)) + result;
2044
- col = Math.floor(col / 26) - 1;
2045
- }
2046
- return result;
2047
- }
2048
- /**
2049
- * Format value for Excel based on data type
2050
- */
2051
- formatExcelValue(value, datatype) {
2052
- if (value === null || value === undefined)
2053
- return '';
2054
- switch (datatype) {
2055
- case 'number':
2056
- case 'currency':
2057
- return typeof value === 'number' ? value : parseFloat(value) || 0;
2058
- case 'date':
2059
- return value instanceof Date ? value : new Date(value);
2060
- default:
2061
- return String(value);
2062
- }
2063
- }
2064
- /**
2065
- * Get Excel value type
2066
- */
2067
- getExcelValueType(value, datatype) {
2068
- if (value === null || value === undefined)
2069
- return 's';
2070
- switch (datatype) {
2071
- case 'number':
2072
- case 'currency':
2073
- return 'n';
2074
- case 'date':
2075
- return 'd';
2076
- default:
2077
- return 's';
2078
- }
2079
- }
2080
- /**
2081
- * Get header cell style
2082
- */
2083
- getHeaderCellStyle(header, rowIndex, maxDepth) {
2084
- const baseStyle = {
2085
- font: { bold: true, color: { rgb: EXCEL_COLORS.TEXT_WHITE } },
2086
- fill: { fgColor: { rgb: this.getHeaderColor(rowIndex, maxDepth) } },
2087
- alignment: { horizontal: 'center', vertical: 'center' },
2088
- border: {
2089
- top: { style: 'thin', color: { rgb: EXCEL_COLORS.BORDER_DARK } },
2090
- bottom: { style: 'thin', color: { rgb: EXCEL_COLORS.BORDER_DARK } },
2091
- left: { style: 'thin', color: { rgb: EXCEL_COLORS.BORDER_DARK } },
2092
- right: { style: 'thin', color: { rgb: EXCEL_COLORS.BORDER_DARK } }
2093
- }
2094
- };
2095
- // Special styling for subtotals
2096
- if (header.isSubtotal) {
2097
- baseStyle.fill.fgColor.rgb = EXCEL_COLORS.SUBTOTAL_HEADER;
2098
- baseStyle.font.color.rgb = EXCEL_COLORS.TEXT_BLACK;
2099
- }
2100
- return baseStyle;
2101
- }
2102
- /**
2103
- * Get data cell style
2104
- */
2105
- getDataCellStyle(row, col, spanInfo) {
2106
- const baseStyle = {
2107
- alignment: { horizontal: 'center', vertical: 'center' },
2108
- border: {
2109
- top: { style: 'thin', color: { rgb: EXCEL_COLORS.BORDER_LIGHT } },
2110
- bottom: { style: 'thin', color: { rgb: EXCEL_COLORS.BORDER_LIGHT } },
2111
- left: { style: 'thin', color: { rgb: EXCEL_COLORS.BORDER_LIGHT } },
2112
- right: { style: 'thin', color: { rgb: EXCEL_COLORS.BORDER_LIGHT } }
2113
- }
2114
- };
2115
- // Special styling for subtotal and grand total rows
2116
- if (row._isSubtotal) {
2117
- baseStyle.fill = { fgColor: { rgb: EXCEL_COLORS.SUBTOTAL_ROW } };
2118
- baseStyle.font = { bold: true };
2119
- }
2120
- else if (row._isGrandTotal) {
2121
- baseStyle.fill = { fgColor: { rgb: EXCEL_COLORS.GRAND_TOTAL_ROW } };
2122
- baseStyle.font = { bold: true };
2123
- }
2124
- // Number formatting
2125
- if (col.datatype === 'number' || col.datatype === 'currency') {
2126
- baseStyle.numFmt = col.datatype === 'currency' ? '$#,##0.00' : '#,##0.00';
2127
- }
2128
- return baseStyle;
2129
- }
2130
- /**
2131
- * Get header color based on row index and depth
2132
- */
2133
- getHeaderColor(rowIndex, maxDepth) {
2134
- const colors = [
2135
- EXCEL_COLORS.HEADER_PRIMARY,
2136
- EXCEL_COLORS.HEADER_SECONDARY,
2137
- EXCEL_COLORS.HEADER_TERTIARY,
2138
- EXCEL_COLORS.HEADER_QUATERNARY
2139
- ];
2140
- return colors[rowIndex % colors.length];
2141
- }
2142
- /**
2143
- * Set column widths based on field definitions
2144
- */
2145
- setExcelColumnWidths(sheet, columnDefinitions) {
2146
- const colWidths = [];
2147
- columnDefinitions.forEach(col => {
2148
- // Convert field_size to Excel column width (Excel uses character units)
2149
- const width = Math.max(col.field_size / 7, 10); // Minimum width of 10
2150
- colWidths.push({ wch: width });
2151
- });
2152
- sheet['!cols'] = colWidths;
2153
- }
2154
- /**
2155
- * Apply additional Excel styling
2156
- */
2157
- applyExcelStyling(sheet, headerStructure, data, configuration) {
2158
- // Set sheet properties
2159
- sheet['!ref'] = this.getExcelRange(headerStructure.maxDepth + data.length + 1, configuration.rows.length + 10);
2160
- // Add freeze panes (freeze header rows)
2161
- sheet['!freeze'] = { xSplit: configuration.rows.length, ySplit: headerStructure.maxDepth };
2162
- // Set print settings
2163
- sheet['!print'] = {
2164
- orientation: 'landscape',
2165
- fitToPage: true,
2166
- fitToWidth: 1,
2167
- fitToHeight: 0
2168
- };
2169
- }
2170
- /**
2171
- * Get Excel range reference
2172
- */
2173
- getExcelRange(maxRow, maxCol) {
2174
- const startCell = this.getExcelCellRef(0, 0);
2175
- const endCell = this.getExcelCellRef(maxRow - 1, maxCol - 1);
2176
- return `${startCell}:${endCell}`;
2177
- }
2178
- /**
2179
- * Parse Excel cell reference to row and column indices
2180
- */
2181
- parseExcelCellRef(cellRef) {
2182
- const match = cellRef.match(/^([A-Z]+)(\d+)$/);
2183
- if (!match) {
2184
- throw new Error(`Invalid cell reference: ${cellRef}`);
2185
- }
2186
- const colStr = match[1];
2187
- const rowStr = match[2];
2188
- // Convert column letters to index
2189
- let col = 0;
2190
- for (let i = 0; i < colStr.length; i++) {
2191
- col = col * 26 + (colStr.charCodeAt(i) - 64);
2192
- }
2193
- col -= 1; // Convert to 0-based index
2194
- const row = parseInt(rowStr) - 1; // Convert to 0-based index
2195
- return { row, col };
2196
- }
1803
+ // Excel/CSV export methods removed
1804
+ // Export functionality should be implemented in the consuming application
1805
+ // Previously removed methods: exportToCsv, exportToExcel, exportToExcelFile, createXlsxSheet,
1806
+ // createSimplifiedXlsxSheet, addXlsxHeaders, addXlsxDataRows, setXlsxColumnWidths,
1807
+ // applyXlsxStyling, getXlsxHeaderStyle, getXlsxDataStyle, formatXlsxValue,
1808
+ // getXlsxValueType, getXlsxRange, generateRowKeyForExcel, and all xlsx helper methods
2197
1809
  /**
2198
1810
  * Initialize column group state based on configuration
2199
1811
  */
@@ -2749,264 +2361,6 @@ class PivotTransformService {
2749
2361
  });
2750
2362
  return groups;
2751
2363
  }
2752
- // ==================== XLSX-COMPATIBLE METHODS ====================
2753
- /**
2754
- * Create simplified xlsx sheet that works reliably without complex merging
2755
- */
2756
- createSimplifiedXlsxSheet(data, columnDefinitions, headerStructure, configuration, options) {
2757
- const sheet = {};
2758
- let rowIndex = 1;
2759
- // Add headers from header structure
2760
- if (options.includeHeaders && headerStructure.headerRows) {
2761
- headerStructure.headerRows.forEach((headerRow, headerRowIndex) => {
2762
- headerRow.forEach((header, colIndex) => {
2763
- const cellRef = this.getExcelCellRef(headerRowIndex, colIndex);
2764
- sheet[cellRef] = {
2765
- v: header.label,
2766
- t: 's',
2767
- s: {
2768
- font: { bold: true, color: { rgb: EXCEL_COLORS.TEXT_WHITE } },
2769
- fill: { fgColor: { rgb: EXCEL_COLORS.HEADER_PRIMARY } },
2770
- alignment: { horizontal: 'center', vertical: 'center' }
2771
- }
2772
- };
2773
- });
2774
- rowIndex++;
2775
- });
2776
- }
2777
- else {
2778
- // Fallback: simple headers from column definitions
2779
- columnDefinitions.forEach((col, colIndex) => {
2780
- const cellRef = this.getExcelCellRef(0, colIndex);
2781
- sheet[cellRef] = {
2782
- v: col.label || col.name,
2783
- t: 's',
2784
- s: {
2785
- font: { bold: true, color: { rgb: EXCEL_COLORS.TEXT_WHITE } },
2786
- fill: { fgColor: { rgb: EXCEL_COLORS.HEADER_PRIMARY } },
2787
- alignment: { horizontal: 'center', vertical: 'center' }
2788
- }
2789
- };
2790
- });
2791
- rowIndex = 2;
2792
- }
2793
- // Add data rows
2794
- data.forEach((row) => {
2795
- columnDefinitions.forEach((col, colIndex) => {
2796
- const cellRef = this.getExcelCellRef(rowIndex - 1, colIndex);
2797
- const value = row[col.name];
2798
- let cellStyle = {
2799
- alignment: { horizontal: 'center', vertical: 'center' }
2800
- };
2801
- // Add styling for special rows
2802
- if (row['isGrandTotal']) {
2803
- cellStyle.font = { bold: true };
2804
- cellStyle.fill = { fgColor: { rgb: EXCEL_COLORS.GRAND_TOTAL_ROW } };
2805
- }
2806
- else if (row['isSubtotal']) {
2807
- cellStyle.font = { bold: true };
2808
- cellStyle.fill = { fgColor: { rgb: EXCEL_COLORS.SUBTOTAL_ROW } };
2809
- }
2810
- sheet[cellRef] = {
2811
- v: this.formatXlsxValue(value, col.datatype),
2812
- t: this.getXlsxValueType(col.datatype),
2813
- s: cellStyle
2814
- };
2815
- });
2816
- rowIndex++;
2817
- });
2818
- // Set range
2819
- const maxCol = columnDefinitions.length - 1;
2820
- const maxRow = rowIndex - 1;
2821
- sheet['!ref'] = `A1:${this.getExcelCellRef(maxRow - 1, maxCol)}`;
2822
- // Set column widths
2823
- sheet['!cols'] = columnDefinitions.map(col => ({
2824
- wch: Math.max(col.label?.length || col.name.length, 15)
2825
- }));
2826
- return sheet;
2827
- }
2828
- /**
2829
- * Add headers to xlsx sheet with proper formatting
2830
- */
2831
- addXlsxHeaders(sheet, headerStructure, merges, options) {
2832
- const { headerRows } = headerStructure;
2833
- // Add each header row
2834
- headerRows.forEach((headerRow, rowIndex) => {
2835
- headerRow.forEach((header, colIndex) => {
2836
- const cellRef = this.getExcelCellRef(rowIndex, colIndex);
2837
- const cell = {
2838
- v: header.label,
2839
- t: 's'
2840
- };
2841
- // Add styling
2842
- if (options.includeStyling) {
2843
- cell.s = this.getXlsxHeaderStyle(header);
2844
- }
2845
- sheet[cellRef] = cell;
2846
- // Handle colspan merging
2847
- if (header.colspan > 1) {
2848
- const endCol = colIndex + header.colspan - 1;
2849
- merges.push({
2850
- s: { r: rowIndex, c: colIndex },
2851
- e: { r: rowIndex, c: endCol }
2852
- });
2853
- }
2854
- // Handle rowspan merging
2855
- if (header.rowspan > 1) {
2856
- const endRow = rowIndex + header.rowspan - 1;
2857
- merges.push({
2858
- s: { r: rowIndex, c: colIndex },
2859
- e: { r: endRow, c: colIndex }
2860
- });
2861
- }
2862
- });
2863
- });
2864
- }
2865
- /**
2866
- * Add data rows to xlsx sheet with rowspan handling
2867
- */
2868
- addXlsxDataRows(sheet, data, columnDefinitions, rowspanInfo, startRow, merges, options) {
2869
- let currentRow = startRow;
2870
- data.forEach((row, rowIndex) => {
2871
- const rowKey = this.generateRowKeyForExcel(row);
2872
- const spanInfo = rowspanInfo.get(rowKey);
2873
- if (spanInfo && spanInfo.skip) {
2874
- return; // Skip this row as it's merged with parent
2875
- }
2876
- // Add row data
2877
- columnDefinitions.forEach((col, colIndex) => {
2878
- const cellRef = this.getExcelCellRef(currentRow, colIndex);
2879
- const value = row[col.name];
2880
- const cell = {
2881
- v: this.formatXlsxValue(value, col.datatype),
2882
- t: this.getXlsxValueType(col.datatype)
2883
- };
2884
- // Add styling
2885
- if (options.includeStyling) {
2886
- cell.s = this.getXlsxDataStyle(row, col);
2887
- }
2888
- sheet[cellRef] = cell;
2889
- });
2890
- // Handle rowspan merging
2891
- if (spanInfo && spanInfo.rowspan > 1) {
2892
- const endRow = currentRow + spanInfo.rowspan - 1;
2893
- columnDefinitions.forEach((col, colIndex) => {
2894
- merges.push({
2895
- s: { r: currentRow, c: colIndex },
2896
- e: { r: endRow, c: colIndex }
2897
- });
2898
- });
2899
- }
2900
- currentRow++;
2901
- });
2902
- }
2903
- /**
2904
- * Set column widths for xlsx sheet
2905
- */
2906
- setXlsxColumnWidths(sheet, columnDefinitions) {
2907
- const colWidths = [];
2908
- columnDefinitions.forEach(col => {
2909
- colWidths.push({
2910
- wch: Math.max(col.name.length, 15) // Minimum width of 15 characters
2911
- });
2912
- });
2913
- sheet['!cols'] = colWidths;
2914
- }
2915
- /**
2916
- * Apply styling to xlsx sheet
2917
- */
2918
- applyXlsxStyling(sheet, headerStructure, data, configuration) {
2919
- // Styling is applied during cell creation in addXlsxHeaders and addXlsxDataRows
2920
- // This method can be used for additional global styling if needed
2921
- }
2922
- /**
2923
- * Get xlsx header cell style
2924
- */
2925
- getXlsxHeaderStyle(header) {
2926
- return {
2927
- font: { bold: true, color: { rgb: EXCEL_COLORS.TEXT_WHITE } },
2928
- fill: { fgColor: { rgb: EXCEL_COLORS.HEADER_PRIMARY } },
2929
- alignment: { horizontal: 'center', vertical: 'center' }
2930
- };
2931
- }
2932
- /**
2933
- * Get xlsx data cell style
2934
- */
2935
- getXlsxDataStyle(row, column) {
2936
- const baseStyle = {
2937
- alignment: { horizontal: 'center', vertical: 'center' }
2938
- };
2939
- // Add specific styling based on row type
2940
- if (row['isGrandTotal']) {
2941
- baseStyle.font = { bold: true };
2942
- baseStyle.fill = { fgColor: { rgb: EXCEL_COLORS.GRAND_TOTAL_ROW } };
2943
- }
2944
- else if (row['isSubtotal']) {
2945
- baseStyle.font = { bold: true };
2946
- baseStyle.fill = { fgColor: { rgb: EXCEL_COLORS.SUBTOTAL_ROW } };
2947
- }
2948
- return baseStyle;
2949
- }
2950
- /**
2951
- * Format value for xlsx
2952
- */
2953
- formatXlsxValue(value, type) {
2954
- if (value === null || value === undefined) {
2955
- return '';
2956
- }
2957
- switch (type) {
2958
- case 'number':
2959
- return typeof value === 'number' ? value : parseFloat(value) || 0;
2960
- case 'date':
2961
- return value instanceof Date ? value : new Date(value);
2962
- default:
2963
- return String(value);
2964
- }
2965
- }
2966
- /**
2967
- * Get xlsx value type
2968
- */
2969
- getXlsxValueType(type) {
2970
- switch (type) {
2971
- case 'number':
2972
- return 'n';
2973
- case 'date':
2974
- return 'd';
2975
- default:
2976
- return 's';
2977
- }
2978
- }
2979
- /**
2980
- * Get xlsx sheet range
2981
- */
2982
- getXlsxRange(sheet) {
2983
- const cells = Object.keys(sheet).filter(key => key.match(/^[A-Z]+\d+$/));
2984
- if (cells.length === 0)
2985
- return null;
2986
- const rows = cells.map(cell => parseInt(cell.replace(/[A-Z]+/, '')));
2987
- const cols = cells.map(cell => this.parseExcelCellRef(cell).col);
2988
- const minRow = Math.min(...rows);
2989
- const maxRow = Math.max(...rows);
2990
- const minCol = Math.min(...cols);
2991
- const maxCol = Math.max(...cols);
2992
- const startCell = this.getExcelCellRef(minRow, minCol);
2993
- const endCell = this.getExcelCellRef(maxRow, maxCol);
2994
- return `${startCell}:${endCell}`;
2995
- }
2996
- /**
2997
- * Generate row key for Excel export
2998
- */
2999
- generateRowKeyForExcel(row) {
3000
- // Create a unique key for the row based on its data
3001
- const keyParts = [];
3002
- // Add dimension values
3003
- Object.keys(row).forEach(key => {
3004
- if (key !== 'isSubtotal' && key !== 'isGrandTotal' && row[key] !== null && row[key] !== undefined) {
3005
- keyParts.push(`${key}:${row[key]}`);
3006
- }
3007
- });
3008
- return keyParts.join('|');
3009
- }
3010
2364
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: PivotTransformService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
3011
2365
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: PivotTransformService, providedIn: 'root' });
3012
2366
  }
@@ -3047,7 +2401,9 @@ class EruGridStore {
3047
2401
  // Pivot-specific signals
3048
2402
  _pivotConfiguration = signal(null, ...(ngDevMode ? [{ debugName: "_pivotConfiguration" }] : []));
3049
2403
  _pivotResult = signal(null, ...(ngDevMode ? [{ debugName: "_pivotResult" }] : []));
3050
- _pivotDrilldown = signal(null, ...(ngDevMode ? [{ debugName: "_pivotDrilldown" }] : []));
2404
+ _drilldown = signal(null, ...(ngDevMode ? [{ debugName: "_drilldown" }] : []));
2405
+ // Data request signal for virtual scrolling pagination
2406
+ _dataRequest = signal(null, ...(ngDevMode ? [{ debugName: "_dataRequest" }] : []));
3051
2407
  // Computed signals
3052
2408
  columns = this._columns.asReadonly();
3053
2409
  groups = this._groups.asReadonly();
@@ -3060,7 +2416,9 @@ class EruGridStore {
3060
2416
  // Pivot-specific readonly signals
3061
2417
  pivotConfiguration = this._pivotConfiguration.asReadonly();
3062
2418
  pivotResult = this._pivotResult.asReadonly();
3063
- pivotDrilldown = this._pivotDrilldown.asReadonly();
2419
+ drilldown = this._drilldown.asReadonly();
2420
+ // Data request readonly signal (exposed to consumers)
2421
+ dataRequest = this._dataRequest.asReadonly();
3064
2422
  // Computed properties
3065
2423
  selectedRows = computed(() => this.rows().filter(row => this.selectedRowIds().has(row.entity_id)), ...(ngDevMode ? [{ debugName: "selectedRows" }] : []));
3066
2424
  totalSelectedCount = computed(() => this.selectedRowIds().size, ...(ngDevMode ? [{ debugName: "totalSelectedCount" }] : []));
@@ -3343,13 +2701,9 @@ class EruGridStore {
3343
2701
  setError(error) {
3344
2702
  this._error.set(error);
3345
2703
  }
3346
- // Data fetching method
3347
- fetchNewRowData(pageSize, page, group) {
3348
- const startIndex = (group.currentPage || 0) * pageSize;
3349
- const endIndex = startIndex + pageSize;
3350
- const data = this.configuration().data?.slice(startIndex, endIndex) || [];
3351
- return of(data).pipe(delay(1000));
3352
- }
2704
+ // Data fetching method - REMOVED
2705
+ // This method has been removed as part of refactoring to signal-based architecture
2706
+ // Data fetching is now handled by the consuming application via dataRequest signal
3353
2707
  // Helper methods
3354
2708
  isRowSelected(rowId) {
3355
2709
  return this.selectedRowIds().has(rowId);
@@ -3454,20 +2808,8 @@ class EruGridStore {
3454
2808
  this._pivotResult.set(null);
3455
2809
  this.transformToPivot();
3456
2810
  }
3457
- exportPivotToCsv() {
3458
- const pivotResult = this.pivotResult();
3459
- if (!pivotResult) {
3460
- return null;
3461
- }
3462
- return this.pivotTransformService.exportToCsv(pivotResult);
3463
- }
3464
- exportPivotToExcel() {
3465
- const pivotResult = this.pivotResult();
3466
- if (!pivotResult) {
3467
- return null;
3468
- }
3469
- return this.pivotTransformService.exportToExcel(pivotResult);
3470
- }
2811
+ // Excel/CSV export methods removed
2812
+ // Export functionality should be implemented in the consuming application
3471
2813
  validatePivotConfiguration(config) {
3472
2814
  return this.gridConfigService.validatePivotConfiguration(config);
3473
2815
  }
@@ -3550,22 +2892,74 @@ class EruGridStore {
3550
2892
  isFreezeGrandTotalEnabled() {
3551
2893
  return this.configuration().config?.freezeGrandTotal ?? false;
3552
2894
  }
3553
- setPivotDrilldown(drilldown, columnValues) {
3554
- if (drilldown.frozenGrandTotalCell) {
3555
- drilldown.rowData = this.pivotGrandTotalData()[0];
2895
+ setDrilldown(drilldown, columnValues) {
2896
+ if (this.isPivotMode()) {
2897
+ if (drilldown.frozenGrandTotalCell) {
2898
+ drilldown.rowData = this.pivotGrandTotalData()[0];
2899
+ }
2900
+ else {
2901
+ drilldown.rowData = this.pivotDisplayData()[parseInt(drilldown.rowId)];
2902
+ }
3556
2903
  }
3557
2904
  else {
3558
- drilldown.rowData = this.pivotDisplayData()[parseInt(drilldown.rowId)];
2905
+ drilldown.rowData = this.rows()[parseInt(drilldown.rowId) - 2];
3559
2906
  }
3560
2907
  const columnValuesArray = columnValues.split('|');
3561
2908
  const cols = this.configuration()?.pivot?.cols;
3562
2909
  columnValuesArray.forEach((value, index) => {
3563
2910
  drilldown.rowData[cols[index]] = value;
3564
2911
  });
3565
- this._pivotDrilldown.set(drilldown);
2912
+ console.log('🔄 SET DRILLDOWN: Drilldown', drilldown);
2913
+ this._drilldown.set(drilldown);
3566
2914
  }
3567
2915
  clearPivotDrilldown() {
3568
- this._pivotDrilldown.set(null);
2916
+ this._drilldown.set(null);
2917
+ }
2918
+ // Data request methods for signal-based virtual scrolling
2919
+ /**
2920
+ * Emit a data request signal (called by grid component)
2921
+ */
2922
+ setDataRequest(request) {
2923
+ console.log('📤 Grid Store: Emitting data request', request);
2924
+ this._dataRequest.set(request);
2925
+ }
2926
+ /**
2927
+ * Clear the data request signal (called by consumer after handling)
2928
+ */
2929
+ clearDataRequest() {
2930
+ this._dataRequest.set(null);
2931
+ }
2932
+ /**
2933
+ * Mark a group as loading (called by grid component before requesting)
2934
+ */
2935
+ startGroupLoading(groupId) {
2936
+ this.updateGroupLoadingState(groupId, true);
2937
+ }
2938
+ /**
2939
+ * Complete loading for a group (called by consumer after data received)
2940
+ */
2941
+ completeGroupLoading(groupId) {
2942
+ this.updateGroupLoadingState(groupId, false);
2943
+ }
2944
+ /**
2945
+ * Add rows for a specific group (called by consumer with API data)
2946
+ */
2947
+ addRowsForGroup(groupId, rows, hasMore) {
2948
+ console.log(`✅ Grid Store: Received ${rows.length} rows for group ${groupId}`);
2949
+ // Add rows to existing rows
2950
+ const existingRows = this.rows();
2951
+ const newRows = [...existingRows, ...rows];
2952
+ this._rows.set(newRows);
2953
+ // Update group state
2954
+ this.updateGroupAfterLoading(groupId, rows.length, !hasMore);
2955
+ }
2956
+ /**
2957
+ * Handle error for data request (called by consumer when API fails)
2958
+ */
2959
+ handleDataRequestError(groupId, error) {
2960
+ console.error(`❌ Grid Store: Data request failed for group ${groupId}`, error);
2961
+ this.updateGroupLoadingState(groupId, false);
2962
+ this.setError(`Failed to load data for group ${groupId}`);
3569
2963
  }
3570
2964
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: EruGridStore, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
3571
2965
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: EruGridStore });
@@ -3871,18 +3265,8 @@ class EruGridService {
3871
3265
  clear_pivot() {
3872
3266
  this.eruGridStore.clearPivot();
3873
3267
  }
3874
- /**
3875
- * Export pivot data to CSV format
3876
- */
3877
- export_pivot_to_csv() {
3878
- return this.eruGridStore.exportPivotToCsv();
3879
- }
3880
- /**
3881
- * Export pivot data to Excel format
3882
- */
3883
- export_pivot_to_excel() {
3884
- return this.eruGridStore.exportPivotToExcel();
3885
- }
3268
+ // Excel/CSV export methods removed
3269
+ // Export functionality should be implemented in the consuming application
3886
3270
  /**
3887
3271
  * Validate pivot configuration
3888
3272
  */
@@ -4544,7 +3928,7 @@ class DataCellComponent {
4544
3928
  columnValue: this.currentValue()
4545
3929
  });
4546
3930
  console.log(columnValues);
4547
- this.eruGridStore().setPivotDrilldown({
3931
+ this.eruGridStore().setDrilldown({
4548
3932
  frozenGrandTotalCell: this.frozenGrandTotalCell(),
4549
3933
  columnName: columnName.join('_'),
4550
3934
  rowId: rowId,
@@ -5500,8 +4884,6 @@ class EruGridComponent {
5500
4884
  initialTotalWidth = 0;
5501
4885
  vp;
5502
4886
  groups = computed(() => this.gridStore.groups(), ...(ngDevMode ? [{ debugName: "groups" }] : []));
5503
- rowLoadQueue = [];
5504
- isProcessingQueue = false;
5505
4887
  // Computed properties using the new GridStore
5506
4888
  groupedRows = computed(() => this.createGroupedRows(), ...(ngDevMode ? [{ debugName: "groupedRows" }] : []));
5507
4889
  columns = computed(() => this.gridStore.columns(), ...(ngDevMode ? [{ debugName: "columns" }] : []));
@@ -5596,34 +4978,39 @@ class EruGridComponent {
5596
4978
  let totalWidth = columns.reduce((total, column) => total + column.field_size, 0);
5597
4979
  return totalWidth;
5598
4980
  }
5599
- getContainerHeight() {
5600
- const element = this.rowContainer?.nativeElement;
5601
- if (element) {
5602
- return element.clientHeight;
4981
+ /* getContainerHeight(): number {
4982
+ const element = this.rowContainer?.nativeElement;
4983
+ if (element) {
4984
+ return element.clientHeight;
5603
4985
  }
5604
- return window.innerHeight; // Fallback to viewport height
5605
- }
4986
+ return window.innerHeight; // Fallback to viewport height
4987
+ } */
5606
4988
  getMinHeightPx() {
5607
4989
  //const parentHeight = this.getContainerHeight();
5608
- let headerHeight = 0;
5609
- let grandTotalHeight = 0;
5610
- let rowHeight = this.gridStore.pivotDisplayData().length * 50;
5611
- // Adjust based on frozen elements
5612
- if (this.freezeHeader()) {
5613
- headerHeight = this.maxDepth() * 35;
5614
- }
5615
- if (this.freezeGrandTotal() && this.grandTotalPosition() === 'before') {
5616
- headerHeight = headerHeight + 50;
4990
+ if (this.gridStore.isPivotMode()) {
4991
+ let headerHeight = 0;
4992
+ let grandTotalHeight = 0;
4993
+ let rowHeight = this.gridStore.pivotDisplayData().length * 50;
4994
+ // Adjust based on frozen elements
4995
+ if (this.freezeHeader()) {
4996
+ headerHeight = this.maxDepth() * 35;
4997
+ }
4998
+ if (this.freezeGrandTotal() && this.grandTotalPosition() === 'before') {
4999
+ headerHeight = headerHeight + 50;
5000
+ }
5001
+ if (this.freezeGrandTotal() && this.grandTotalPosition() === 'after') {
5002
+ grandTotalHeight = 60;
5003
+ }
5004
+ let minHeight = (this.gridHeight() - headerHeight); // Start with 50% of parent
5005
+ // Ensure minimum usable height
5006
+ minHeight = Math.min(minHeight, rowHeight);
5007
+ minHeight = minHeight + 1;
5008
+ // Don't exceed parent height
5009
+ return minHeight;
5617
5010
  }
5618
- if (this.freezeGrandTotal() && this.grandTotalPosition() === 'after') {
5619
- grandTotalHeight = 60;
5011
+ else {
5012
+ return this.gridHeight();
5620
5013
  }
5621
- let minHeight = (this.gridHeight() - headerHeight); // Start with 50% of parent
5622
- // Ensure minimum usable height
5623
- minHeight = Math.min(minHeight, rowHeight);
5624
- minHeight = minHeight + 1;
5625
- // Don't exceed parent height
5626
- return minHeight;
5627
5014
  }
5628
5015
  adjustScrollWidth() {
5629
5016
  let headerHeight = 0;
@@ -5639,14 +5026,16 @@ class EruGridComponent {
5639
5026
  * Get the rendered range (what's in the DOM including buffer)
5640
5027
  * @returns Object with start, end, and count of rendered items
5641
5028
  */
5642
- getRenderedRange() {
5643
- if (!this.viewport) {
5644
- return { start: 0, end: 0, count: 0 };
5645
- }
5646
- const range = this.viewport.getRenderedRange();
5647
- const count = range.end - range.start;
5648
- return { start: range.start, end: range.end, count };
5649
- }
5029
+ /* getRenderedRange(): { start: number; end: number; count: number } {
5030
+ if (!this.viewport) {
5031
+ return { start: 0, end: 0, count: 0 };
5032
+ }
5033
+
5034
+ const range = this.viewport.getRenderedRange();
5035
+ const count = range.end - range.start;
5036
+
5037
+ return { start: range.start, end: range.end, count };
5038
+ } */
5650
5039
  ROWS_PER_PAGE = 50;
5651
5040
  SCROLL_THRESHOLD = 10;
5652
5041
  lastLoadedGroupIds = new Set();
@@ -5724,7 +5113,7 @@ class EruGridComponent {
5724
5113
  const rows = this.gridStore.rows();
5725
5114
  const groups = this.gridStore.groups();
5726
5115
  console.log("groups", groups);
5727
- console.log("rows", rows);
5116
+ console.log("rows", rows?.length);
5728
5117
  const flattenedRows = [];
5729
5118
  if (groups.length > 0) {
5730
5119
  groups.forEach(group => {
@@ -5754,70 +5143,25 @@ class EruGridComponent {
5754
5143
  flattenedRows.push({ type: 'row', group: undefined, row });
5755
5144
  });
5756
5145
  }
5757
- console.log("flattenedRows", flattenedRows);
5146
+ console.log("flattenedRows", flattenedRows?.length);
5758
5147
  return flattenedRows;
5759
5148
  }
5760
5149
  initializeGroups() {
5761
5150
  this.fetchInitialRows();
5762
5151
  }
5763
5152
  fetchInitialRows() {
5153
+ console.log("fetchInitialRows", this.gridStore.groups());
5764
5154
  this.gridStore.groups().forEach(group => {
5765
5155
  if (group.isExpanded) {
5766
- this.fetchRowsForGroup(group.id, 0, group.totalRowCount < this.ROWS_PER_PAGE ? group.totalRowCount : this.ROWS_PER_PAGE);
5156
+ this.requestRowsForGroup(group);
5767
5157
  }
5768
5158
  });
5769
5159
  }
5770
- fetchRowsForGroup(groupId, page, pageSize = this.ROWS_PER_PAGE) {
5771
- return new Promise((resolve, reject) => {
5772
- const group = this.gridStore.groups().find(g => g.id === groupId);
5773
- if (!group || !group.hasMoreRows) {
5774
- resolve();
5775
- return;
5776
- }
5777
- if (group.isLoading) {
5778
- resolve();
5779
- return;
5780
- }
5781
- this.gridStore.updateGroupLoadingState(groupId, true);
5782
- const skip = page * pageSize;
5783
- const loadTimeout = setTimeout(() => {
5784
- this.gridStore.updateGroupLoadingState(groupId, false);
5785
- reject(new Error('Task loading timed out'));
5786
- }, 10000);
5787
- this.gridStore.fetchNewRowData(pageSize, skip, group).subscribe({
5788
- next: (response) => {
5789
- clearTimeout(loadTimeout);
5790
- if (!response || response.length === 0) {
5791
- this.gridStore.updateGroupAfterLoading(groupId, 0, true);
5792
- resolve();
5793
- return;
5794
- }
5795
- const newRows = this.normalizeRowData(response.map((r) => ({ ...r, groupId })), group);
5796
- this.gridStore.addRows(newRows);
5797
- this.gridStore.updateGroupAfterLoading(groupId, newRows.length);
5798
- this.changeDetectorRef.markForCheck();
5799
- resolve();
5800
- },
5801
- error: (error) => {
5802
- clearTimeout(loadTimeout);
5803
- console.error(`Error loading tasks for group ${groupId}:`, error);
5804
- this.gridStore.updateGroupLoadingState(groupId, false);
5805
- this.markGroupAsNoMoreRows(groupId);
5806
- reject(error);
5807
- }
5808
- });
5809
- });
5810
- }
5160
+ // fetchRowsForGroup - REMOVED
5161
+ // Data fetching is now handled by the consuming application via signal
5162
+ // See requestRowsForGroup() method below for the new approach
5811
5163
  onScroll(scrollIndex) {
5812
- const groups = this.gridStore.groups();
5813
- const visibleGroups = this.getVisibleGroups();
5814
- visibleGroups.forEach(group => {
5815
- if (group.isExpanded &&
5816
- group.hasMoreRows &&
5817
- !group.isLoading) {
5818
- this.checkGroupRowLoading(group);
5819
- }
5820
- });
5164
+ console.log("onScroll called", scrollIndex);
5821
5165
  this.checkAndLoadMoreRows();
5822
5166
  }
5823
5167
  getVisibleGroups() {
@@ -5836,7 +5180,7 @@ class EruGridComponent {
5836
5180
  .filter((group) => group !== undefined);
5837
5181
  }
5838
5182
  getGroupItemAtIndex(index) {
5839
- const groupItems = this.createGroupedRows();
5183
+ const groupItems = this.groupedRows();
5840
5184
  return groupItems[index];
5841
5185
  }
5842
5186
  checkAndLoadMoreRows() {
@@ -5845,6 +5189,7 @@ class EruGridComponent {
5845
5189
  const renderedRange = this.viewport.getRenderedRange();
5846
5190
  const dataLength = this.viewport.getDataLength();
5847
5191
  const isNearEnd = renderedRange.end >= (dataLength - this.SCROLL_THRESHOLD);
5192
+ console.log("isNearEnd", renderedRange, dataLength, isNearEnd);
5848
5193
  if (isNearEnd) {
5849
5194
  this.intelligentRowLoading();
5850
5195
  }
@@ -5856,38 +5201,41 @@ class EruGridComponent {
5856
5201
  !group.isLoading &&
5857
5202
  !this.lastLoadedGroupIds.has(group.id));
5858
5203
  const sortedGroupsToLoad = groupsToLoad.sort((a, b) => (a.currentLoadedRows || 0) - (b.currentLoadedRows || 0));
5204
+ console.log("sortedGroupsToLoad", sortedGroupsToLoad);
5859
5205
  if (sortedGroupsToLoad.length > 0) {
5860
5206
  const groupToLoad = sortedGroupsToLoad[0];
5861
- this.lastLoadedGroupIds.add(groupToLoad.id);
5862
- this.queueRowLoading(groupToLoad);
5863
- setTimeout(() => {
5864
- this.lastLoadedGroupIds.delete(groupToLoad.id);
5865
- }, 5000);
5866
- }
5867
- }
5868
- checkGroupRowLoading(group) {
5869
- const shouldLoadRows = group.currentLoadedRows < group.totalRowCount &&
5870
- !this.lastLoadedGroupIds.has(group.id);
5871
- if (shouldLoadRows) {
5872
- this.queueRowLoading(group);
5873
- }
5874
- }
5875
- queueRowLoading(group) {
5876
- if (!group) {
5877
- const groups = this.gridStore.groups();
5878
- group = groups.find(g => g.isExpanded &&
5879
- g.hasMoreRows &&
5880
- !g.isLoading);
5881
- }
5882
- if (group) {
5883
- this.rowLoadQueue.push({
5884
- groupId: group.id,
5885
- page: group.currentPage || 0,
5886
- pageSize: group.totalRowCount < this.ROWS_PER_PAGE ? group.totalRowCount : this.ROWS_PER_PAGE
5887
- });
5888
- this.processRowLoadQueue();
5207
+ console.log("groupToLoad", groupToLoad);
5208
+ this.requestRowsForGroup(groupToLoad);
5889
5209
  }
5890
5210
  }
5211
+ // queueRowLoading - REPLACED with requestRowsForGroup
5212
+ // This method has been replaced with direct signal emission
5213
+ /**
5214
+ * Request rows for a specific group via signal
5215
+ */
5216
+ requestRowsForGroup(group) {
5217
+ // Prevent duplicate requests
5218
+ console.log("requestRowsForGroup", group);
5219
+ if (this.lastLoadedGroupIds.has(group.id))
5220
+ return;
5221
+ const request = {
5222
+ groupId: group.id,
5223
+ page: group.currentPage || 0,
5224
+ pageSize: group.totalRowCount < this.ROWS_PER_PAGE ? group.totalRowCount : this.ROWS_PER_PAGE,
5225
+ timestamp: Date.now(),
5226
+ groupTitle: group.title
5227
+ };
5228
+ console.log('📤 Grid Component: Emitting data request', request);
5229
+ // Emit signal via store
5230
+ this.gridStore.setDataRequest(request);
5231
+ // Mark group as loading
5232
+ this.gridStore.startGroupLoading(group.id);
5233
+ // Prevent duplicate requests for 5 seconds
5234
+ this.lastLoadedGroupIds.add(group.id);
5235
+ setTimeout(() => {
5236
+ this.lastLoadedGroupIds.delete(group.id);
5237
+ }, 1000);
5238
+ }
5891
5239
  trackByColumnFn(index, column) {
5892
5240
  return column.name;
5893
5241
  }
@@ -5951,35 +5299,36 @@ class EruGridComponent {
5951
5299
  this.gridStore.setColumns(normalizedColumns);
5952
5300
  }
5953
5301
  // Enhanced method to normalize row data
5954
- normalizeRowData(rows, group) {
5955
- return rows.map(row => ({
5956
- ...row,
5957
- groupTitle: group?.title,
5958
- displayValue: this.getDisplayValue(row),
5959
- height: 50
5960
- }));
5961
- }
5962
- getDisplayValue(row, column) {
5963
- if (!column) {
5964
- return this.getGenericRowDisplay(row);
5965
- }
5966
- const value = row[column];
5967
- if (value === null || value === undefined)
5968
- return '';
5969
- if (typeof value === 'string')
5970
- return value.trim();
5971
- if (typeof value === 'number')
5972
- return value.toString();
5973
- return JSON.stringify(value);
5974
- }
5975
- getGenericRowDisplay(row) {
5976
- const displayProps = ['title', 'id', 'status'];
5977
- return displayProps
5978
- .map(prop => row[prop])
5979
- .filter(value => value != null)
5980
- .join(' - ')
5981
- .trim();
5982
- }
5302
+ /* private normalizeRowData(rows: any[], group?: RowGroup): any[] {
5303
+ return rows.map(row => ({
5304
+ ...row,
5305
+
5306
+ groupTitle: group?.title,
5307
+
5308
+ displayValue: this.getDisplayValue(row),
5309
+ height: 50
5310
+ }));
5311
+ } */
5312
+ /* private getDisplayValue(row: any, column?: string): string {
5313
+ if (!column) {
5314
+ return this.getGenericRowDisplay(row);
5315
+ }
5316
+
5317
+ const value = row[column];
5318
+ if (value === null || value === undefined) return '';
5319
+ if (typeof value === 'string') return value.trim();
5320
+ if (typeof value === 'number') return value.toString();
5321
+
5322
+ return JSON.stringify(value);
5323
+ } */
5324
+ /* private getGenericRowDisplay(row: any): string {
5325
+ const displayProps = ['title', 'id', 'status'];
5326
+ return displayProps
5327
+ .map(prop => row[prop])
5328
+ .filter(value => value != null)
5329
+ .join(' - ')
5330
+ .trim();
5331
+ } */
5983
5332
  toggleRowSelection(event, row) {
5984
5333
  event.stopPropagation();
5985
5334
  const checkbox = event.target;
@@ -6024,45 +5373,24 @@ class EruGridComponent {
6024
5373
  getSelectedRows() {
6025
5374
  return Array.from(this.gridStore.selectedRowIds());
6026
5375
  }
6027
- markGroupAsNoMoreRows(groupId) {
6028
- // This functionality is now handled by GridStore.updateGroupAfterLoading
6029
- this.gridStore.updateGroupAfterLoading(groupId, 0, true);
6030
- }
5376
+ /* private markGroupAsNoMoreRows(groupId: string) {
5377
+ // This functionality is now handled by GridStore.updateGroupAfterLoading
5378
+ this.gridStore.updateGroupAfterLoading(groupId, 0, true);
5379
+ } */
6031
5380
  toggleGroupExpansion(groupId) {
6032
5381
  const group = this.gridStore.groups().find(g => g.id === groupId);
6033
5382
  if (!group)
6034
5383
  return;
6035
5384
  const newExpandedState = !group.isExpanded;
6036
5385
  this.gridStore.toggleGroupExpansion(groupId);
5386
+ // Request data when expanding group with no data
6037
5387
  if (newExpandedState && (!group.rows || group.rows.length === 0)) {
6038
- this.fetchRowsForGroup(groupId, 0, group.totalRowCount < this.ROWS_PER_PAGE ? group.totalRowCount : this.ROWS_PER_PAGE)
6039
- .catch(error => {
6040
- console.error(`Error loading rows for expanded group ${groupId}:`, error);
6041
- });
6042
- }
6043
- }
6044
- processRowLoadQueue() {
6045
- if (this.isProcessingQueue)
6046
- return;
6047
- if (this.rowLoadQueue.length === 0) {
6048
- this.isProcessingQueue = false;
6049
- return;
6050
- }
6051
- this.isProcessingQueue = true;
6052
- const nextLoad = this.rowLoadQueue.shift();
6053
- if (nextLoad) {
6054
- this.fetchRowsForGroup(nextLoad.groupId, nextLoad.page, nextLoad.pageSize)
6055
- .then(() => {
6056
- this.isProcessingQueue = false;
6057
- this.processRowLoadQueue();
6058
- })
6059
- .catch(error => {
6060
- console.error('Error loading rows:', error);
6061
- this.isProcessingQueue = false;
6062
- this.processRowLoadQueue();
6063
- });
5388
+ this.requestRowsForGroup(group);
6064
5389
  }
6065
5390
  }
5391
+ // processRowLoadQueue - REMOVED
5392
+ // Queue-based loading is replaced with direct signal emission
5393
+ // See requestRowsForGroup() method for the new approach
6066
5394
  // Collapsible column methods
6067
5395
  /**
6068
5396
  * Toggle collapse/expand state of a column group
@@ -6149,94 +5477,103 @@ class EruGridComponent {
6149
5477
  * Get rowspan value for a specific cell - VIRTUAL SCROLLING COMPATIBLE
6150
5478
  * This is a compatibility wrapper around getEffectiveRowspan
6151
5479
  */
6152
- getCellRowspan(rowIndex, columnName) {
6153
- const effectiveRowspan = this.getEffectiveRowspan(rowIndex, columnName);
6154
- return effectiveRowspan || 1;
6155
- }
5480
+ /* getCellRowspan(rowIndex: number, columnName: string): number {
5481
+ const effectiveRowspan = this.getEffectiveRowspan(rowIndex, columnName);
5482
+ return effectiveRowspan || 1;
5483
+ } */
6156
5484
  /**
6157
5485
  * Get columns that should be frozen on the left (all row dimensions up to and including freezeField)
6158
5486
  */
6159
- getRowDimensionColumns() {
6160
- if (!this.gridStore.isFreezeEnabled())
6161
- return [];
6162
- const freezeField = this.gridStore.getFreezeField();
6163
- if (!freezeField)
6164
- return [];
6165
- const allColumns = this.getLeafColumns();
6166
- const pivotConfig = this.gridStore.pivotConfiguration();
6167
- if (!pivotConfig?.rows)
6168
- return [];
6169
- // Find the index of the freeze field in the row dimensions
6170
- const freezeFieldIndex = pivotConfig.rows.indexOf(freezeField);
6171
- if (freezeFieldIndex === -1)
6172
- return [];
6173
- // Return all row dimension columns up to and including the freeze field
6174
- const rowDimensionsToFreeze = pivotConfig.rows.slice(0, freezeFieldIndex + 1);
6175
- return allColumns.filter(col => rowDimensionsToFreeze.includes(col.name));
6176
- }
5487
+ /* getRowDimensionColumns(): any[] {
5488
+ if (!this.gridStore.isFreezeEnabled()) return [];
5489
+
5490
+ const freezeField = this.gridStore.getFreezeField();
5491
+ if (!freezeField) return [];
5492
+
5493
+ const allColumns = this.getLeafColumns();
5494
+ const pivotConfig = this.gridStore.pivotConfiguration();
5495
+
5496
+ if (!pivotConfig?.rows) return [];
5497
+
5498
+ // Find the index of the freeze field in the row dimensions
5499
+ const freezeFieldIndex = pivotConfig.rows.indexOf(freezeField);
5500
+ if (freezeFieldIndex === -1) return [];
5501
+
5502
+ // Return all row dimension columns up to and including the freeze field
5503
+ const rowDimensionsToFreeze = pivotConfig.rows.slice(0, freezeFieldIndex + 1);
5504
+ return allColumns.filter(col => rowDimensionsToFreeze.includes(col.name));
5505
+ } */
6177
5506
  /**
6178
5507
  * Get columns that should be scrollable on the right (all except frozen fields)
6179
5508
  */
6180
- getColumnDimensionColumns() {
6181
- const allColumns = this.getLeafColumns();
6182
- if (!this.gridStore.isFreezeEnabled())
6183
- return allColumns;
6184
- const freezeField = this.gridStore.getFreezeField();
6185
- if (!freezeField)
6186
- return allColumns;
6187
- const pivotConfig = this.gridStore.pivotConfiguration();
6188
- if (!pivotConfig?.rows)
6189
- return allColumns;
6190
- // Find the index of the freeze field in the row dimensions
6191
- const freezeFieldIndex = pivotConfig.rows.indexOf(freezeField);
6192
- if (freezeFieldIndex === -1)
6193
- return allColumns;
6194
- // Get all row dimensions that should be frozen
6195
- const rowDimensionsToFreeze = pivotConfig.rows.slice(0, freezeFieldIndex + 1);
6196
- // Return all columns except the frozen row dimensions
6197
- return allColumns.filter(col => !rowDimensionsToFreeze.includes(col.name));
6198
- }
5509
+ /* getColumnDimensionColumns(): any[] {
5510
+ const allColumns = this.getLeafColumns();
5511
+
5512
+ if (!this.gridStore.isFreezeEnabled()) return allColumns;
5513
+
5514
+ const freezeField = this.gridStore.getFreezeField();
5515
+ if (!freezeField) return allColumns;
5516
+
5517
+ const pivotConfig = this.gridStore.pivotConfiguration();
5518
+ if (!pivotConfig?.rows) return allColumns;
5519
+
5520
+ // Find the index of the freeze field in the row dimensions
5521
+ const freezeFieldIndex = pivotConfig.rows.indexOf(freezeField);
5522
+ if (freezeFieldIndex === -1) return allColumns;
5523
+
5524
+ // Get all row dimensions that should be frozen
5525
+ const rowDimensionsToFreeze = pivotConfig.rows.slice(0, freezeFieldIndex + 1);
5526
+
5527
+ // Return all columns except the frozen row dimensions
5528
+ return allColumns.filter(col => !rowDimensionsToFreeze.includes(col.name));
5529
+ } */
6199
5530
  /**
6200
5531
  * Get headers that should be frozen (all row dimension headers up to and including freezeField)
6201
5532
  */
6202
- getRowDimensionHeaders(headerRow) {
6203
- if (!this.gridStore.isFreezeEnabled())
6204
- return [];
6205
- const freezeField = this.gridStore.getFreezeField();
6206
- if (!freezeField)
6207
- return [];
6208
- const pivotConfig = this.gridStore.pivotConfiguration();
6209
- if (!pivotConfig?.rows)
6210
- return [];
6211
- // Find the index of the freeze field in the row dimensions
6212
- const freezeFieldIndex = pivotConfig.rows.indexOf(freezeField);
6213
- if (freezeFieldIndex === -1)
6214
- return [];
6215
- // Get all row dimensions that should be frozen
6216
- const rowDimensionsToFreeze = pivotConfig.rows.slice(0, freezeFieldIndex + 1);
6217
- return headerRow.filter(header => rowDimensionsToFreeze.includes(header.name) ||
6218
- (header.name && rowDimensionsToFreeze.includes(header.name)));
6219
- }
5533
+ /* getRowDimensionHeaders(headerRow: any[]): any[] {
5534
+ if (!this.gridStore.isFreezeEnabled()) return [];
5535
+
5536
+ const freezeField = this.gridStore.getFreezeField();
5537
+ if (!freezeField) return [];
5538
+
5539
+ const pivotConfig = this.gridStore.pivotConfiguration();
5540
+ if (!pivotConfig?.rows) return [];
5541
+
5542
+ // Find the index of the freeze field in the row dimensions
5543
+ const freezeFieldIndex = pivotConfig.rows.indexOf(freezeField);
5544
+ if (freezeFieldIndex === -1) return [];
5545
+
5546
+ // Get all row dimensions that should be frozen
5547
+ const rowDimensionsToFreeze = pivotConfig.rows.slice(0, freezeFieldIndex + 1);
5548
+
5549
+ return headerRow.filter(header =>
5550
+ rowDimensionsToFreeze.includes(header.name) ||
5551
+ (header.name && rowDimensionsToFreeze.includes(header.name))
5552
+ );
5553
+ } */
6220
5554
  /**
6221
5555
  * Get headers that should be scrollable (all except frozen fields)
6222
5556
  */
6223
- getColumnDimensionHeaders(headerRow) {
6224
- if (!this.gridStore.isFreezeEnabled())
6225
- return headerRow;
6226
- const freezeField = this.gridStore.getFreezeField();
6227
- if (!freezeField)
6228
- return headerRow;
6229
- const pivotConfig = this.gridStore.pivotConfiguration();
6230
- if (!pivotConfig?.rows)
6231
- return headerRow;
6232
- // Find the index of the freeze field in the row dimensions
6233
- const freezeFieldIndex = pivotConfig.rows.indexOf(freezeField);
6234
- if (freezeFieldIndex === -1)
6235
- return headerRow;
6236
- // Get all row dimensions that should be frozen
6237
- const rowDimensionsToFreeze = pivotConfig.rows.slice(0, freezeFieldIndex + 1);
6238
- return headerRow.filter(header => !rowDimensionsToFreeze.includes(header.name));
6239
- }
5557
+ /* getColumnDimensionHeaders(headerRow: any[]): any[] {
5558
+ if (!this.gridStore.isFreezeEnabled()) return headerRow;
5559
+
5560
+ const freezeField = this.gridStore.getFreezeField();
5561
+ if (!freezeField) return headerRow;
5562
+
5563
+ const pivotConfig = this.gridStore.pivotConfiguration();
5564
+ if (!pivotConfig?.rows) return headerRow;
5565
+
5566
+ // Find the index of the freeze field in the row dimensions
5567
+ const freezeFieldIndex = pivotConfig.rows.indexOf(freezeField);
5568
+ if (freezeFieldIndex === -1) return headerRow;
5569
+
5570
+ // Get all row dimensions that should be frozen
5571
+ const rowDimensionsToFreeze = pivotConfig.rows.slice(0, freezeFieldIndex + 1);
5572
+
5573
+ return headerRow.filter(header =>
5574
+ !rowDimensionsToFreeze.includes(header.name)
5575
+ );
5576
+ } */
6240
5577
  /**
6241
5578
  * Check if a column should be sticky (frozen on the left)
6242
5579
  */
@@ -6424,7 +5761,7 @@ class EruGridComponent {
6424
5761
  return this.columns().length;
6425
5762
  }
6426
5763
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: EruGridComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
6427
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.3", type: EruGridComponent, isStandalone: true, selector: "eru-grid", inputs: { gridConfig: "gridConfig" }, providers: [EruGridStore, EruGridService], viewQueries: [{ propertyName: "viewport", first: true, predicate: CdkVirtualScrollViewport, descendants: true }, { propertyName: "rowContainer", first: true, predicate: ["rowContainer"], descendants: true }, { propertyName: "headerScroller", first: true, predicate: ["headerScroller"], descendants: true, read: ElementRef }, { propertyName: "gtScroller", first: true, predicate: ["gtScroller"], descendants: true, read: ElementRef }, { propertyName: "vp", first: true, predicate: ["vp"], descendants: true }], ngImport: i0, template: "<!-- <div style=\"background: #f0f0f0; font-size: 12px; border-bottom: 1px solid #ccc;\">\n currentPivotScrollIndex {{currentPivotScrollIndex()}} | \n firstDataRowIndex {{firstDataRowIndex()}} | \n firstTr {{firstTr}} |\n maxDepth {{maxDepth()}}\n</div> -->\n<div class=\"incremental-row-container eru-grid\" #rowContainer \n [class.pivot-mode]=\"gridStore.isPivotMode()\"\n [class.table-mode]=\"!gridStore.isPivotMode()\">\n \n <!-- Pivot Mode Template -->\n @if (gridStore.isPivotMode()) {\n <ng-container >\n <div class=\"pivot-container\" style=\"display: flex; flex-direction: column; height: 100%;\"\n [style]=\"'--table-min-height: ' + getInitialMinHeightPx() + 'px; --table-total-width: ' + getInitialTotalWidth() + 'px'\">\n <!-- Debug info for first visible row -->\n \n \n <div class=\"pivot-single-table\" style=\"height: 100%; width: 100%; overflow: hidden; display: flex; flex-direction: column;\">\n @if (freezeHeader()) {\n <div #headerScroller class=\"header-shell\">\n <table class=\"eru-grid-table pivot-table\" \n [style]=\"'width: auto; min-width: 100%; --table-total-width: ' + getInitialTotalWidth() + 'px'\"\n [class.show-column-lines]=\"showColumnLines()\" \n [class.show-row-lines]=\"showRowLines()\">\n <!-- Column Groups for consistent width -->\n <ng-container *ngTemplateOutlet=\"pivotColGroup\"></ng-container>\n \n <ng-container *ngTemplateOutlet=\"pivotTableHead\"></ng-container>\n @if(grandTotalPosition() === 'before' && freezeGrandTotal()) {\n <ng-container *ngTemplateOutlet=\"pivotGrandTotal\"></ng-container>\n }\n </table> \n </div>\n } \n <!-- Virtual Scrolled Table Body -->\n <div>\n <cdk-virtual-scroll-viewport\n #vp\n [itemSize]=\"50\"\n class=\"viewport pivot-viewport\"\n [class.apply-cdk-width]=\"applyCdkWidth()\"\n (scrolledIndexChange)=\"onPivotScroll($event)\"\n (scroll)=\"onBodyScroll($event)\"\n style=\"overflow: auto;\">\n <table class=\"eru-grid-table pivot-table\" \n [style]=\"'width: auto; min-width: 100%; --table-total-width: ' + getInitialTotalWidth() + 'px'\"\n [class.show-column-lines]=\"showColumnLines()\" \n [class.show-row-lines]=\"showRowLines()\"\n >\n <!-- Column Groups for consistent width -->\n <ng-container *ngTemplateOutlet=\"pivotColGroup\"></ng-container>\n \n @if (!freezeHeader()) {\n <ng-container *ngTemplateOutlet=\"pivotTableHead\"></ng-container> \n }\n <!-- Table Body with Virtual Scrolling -->\n <tbody class=\"pivot-tbody\">\n \n <tr *cdkVirtualFor=\"let pivotRow of gridStore.pivotDisplayData(); \n trackBy: trackByPivotRowFn; \n let i = index\"\n class=\"pivot-row\"\n [class.subtotal-row]=\"pivotRow._isSubtotal\"\n [class.grand-total-row]=\"pivotRow._isGrandTotal\"\n [class.subtotal-bold]=\"pivotRow._isSubtotal && subTotalStyle() === 'bold'\"\n [class.subtotal-italic]=\"pivotRow._isSubtotal && subTotalStyle() === 'italic'\"\n [class.subtotal-highlighted]=\"pivotRow._isSubtotal && subTotalStyle() === 'highlighted'\"\n [class.grand-total-bold]=\"pivotRow._isGrandTotal && grandTotalStyle() === 'bold'\"\n [class.grand-total-italic]=\"pivotRow._isGrandTotal && grandTotalStyle() === 'italic'\"\n [class.grand-total-highlighted]=\"pivotRow._isGrandTotal && grandTotalStyle() === 'highlighted'\"\n [style.height.px]=\"50\"\n [attr.data-pivot-row]=\"i\">\n @if ((!pivotRow._isGrandTotal && freezeGrandTotal() ) || (!freezeGrandTotal() )) { \n @for (column of getLeafColumns(); track trackByColumnFn($index, column); let colIndex = $index) {\n @if (!shouldSkipCell(i, column.name)) {\n <td\n [attr.rowspan]=\"getEffectiveRowspan(i, column.name)\"\n [style.width.px]=\"column.field_size\"\n [style.minWidth.px]=\"column.field_size\"\n class=\"pivot-cell\"\n [class.row-dimension-cell]=\"isRowDimensionColumn(column.name)\"\n [class.column-dimension-cell]=\"!isRowDimensionColumn(column.name)\"\n [class.aggregated-value]=\"!isRowDimensionColumn(column.name) && column.datatype === 'number'\"\n [class.rowspan-cell]=\"getEffectiveRowspan(i, column.name) || 1 > 1\"\n [class.sticky-column]=\"isStickyColumn(column.name, colIndex)\"\n [style.position]=\"isStickyColumn(column.name, colIndex) ? 'sticky' : 'static'\"\n [style.left.px]=\"getStickyColumnLeft(column.name, colIndex)\"\n [style.z-index]=\"isStickyColumn(column.name, colIndex) ? 99 : 1\"\n [style.height.px]=\"50\"\n [attr.xx]=\"i\"> \n <div class=\"cell-content pivot-cell-content\">\n <data-cell\n [class.aggregation]=\"!!column.aggregationFunction\"\n [fieldSize]=\"column.field_size\"\n [columnDatatype]=\"column.datatype\"\n [columnName]=\"column.name\"\n [value]=\"getEffectiveCellValue(i,column.name, pivotRow)\"\n [column]=\"column\"\n [drillable]=\"column.enableDrilldown || false\"\n [mode]=\"mode()\"\n [isEditable]=\"isEditable()\"\n [id]=\"'pivot_' + i + '_' + column.name\"\n [eruGridStore]=\"gridStore\">\n </data-cell>\n </div>\n </td>\n }\n }\n } @else {\n <td [style.height.px]=\"50\" [attr.colspan]=\"getLeafColumns().length\"> </td>\n }\n </tr> \n </tbody>\n </table>\n </cdk-virtual-scroll-viewport>\n\n </div>\n @if (freezeGrandTotal() && grandTotalPosition() === 'after') {\n <div #gtScroller class=\"header-shell gt-shell\"\n [class.adjust-bottom]=\"!applyCdkWidth()\"\n [class.adjust-bottom-vs]=\"adjustScrollWidth()\">\n <table class=\"eru-grid-table pivot-table\" \n [style]=\"'width: auto; min-width: 100%; --table-total-width: ' + getInitialTotalWidth() + 'px'\"\n [class.show-column-lines]=\"showColumnLines()\" \n [class.show-row-lines]=\"showRowLines()\">\n <ng-container *ngTemplateOutlet=\"pivotColGroup\"></ng-container>\n \n <ng-container *ngTemplateOutlet=\"pivotGrandTotal\"></ng-container>\n \n </table> \n </div>\n }\n \n\n </div>\n </div>\n </ng-container>\n} @else {\n \n<!-- Table Mode Template -->\n <ng-container>\n <cdk-virtual-scroll-viewport\n [itemSize]=\"30\"\n class=\"viewport table-viewport\"\n (scrolledIndexChange)=\"onScroll($event)\"\n >\n <div class=\"table-wrapper\">\n \n <table class=\"eru-grid-table\" [class.show-column-lines]=\"showColumnLines()\" [class.show-column-lines]=\"showColumnLines()\">\n <thead>\n <tr>\n @if(gridStore.configuration().config.allowSelection) {\n <th class=\"checkbox-column\"></th>\n }\n <th *ngFor=\"let column of columns(); trackBy: trackByColumnFn\"\n [style.width.px]=\"column.field_size\"\n [resizeColumn]=\"gridStore.isFeatureEnabled('columnResizable')\"\n [style.minWidth.px]=\"column.field_size\"\n class=\"column-header\">\n {{column.label}}\n </th>\n </tr>\n </thead>\n <tbody *ngIf=\"columns() as columnsList\">\n <ng-container *cdkVirtualFor=\"let groupItem of groupedRows();\n trackBy: trackByGroupItemFn;\n let i = index;\n let first=first\">\n <tr\n *ngIf=\"groupItem.type === 'header' && groups().length > 1\"\n class=\"group-header\"\n (click)=\"toggleGroupExpansion(groupItem.group?.id || '')\"\n >\n \n <td class=\"checkbox-column\" style=\"border: none;\">\n {{ groupItem.group?.isExpanded ? '\u25BC' : '\u25B6' }}\n </td>\n \n <td [attr.colspan]=\"2\" style=\"border: none;\">\n <span class=\"group-title\">\n {{ groupItem.group?.title }}\n </span>\n <span class=\"group-row-count\">\n ({{ groupItem.group?.currentLoadedRows || 0 }}/{{ groupItem.group?.totalRowCount || 0 }})\n </span>\n </td>\n </tr>\n <tr *ngIf=\"groupItem.type === 'table-header' && groups().length > 1\" style=\"background:#fafafa\">\n @if(gridStore.configuration().config.allowSelection) {\n <th class=\"checkbox-column\" style=\"text-align: center;\">\n <input\n type=\"checkbox\"\n [checked]=\"isGroupSelected(groupItem.group?.id || '')\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"toggleGroupSelection($event, groupItem.group?.id || '')\"\n >\n </th>\n }\n <th *ngFor=\"let column of columns(); trackBy: trackByColumnFn;let i =index\"\n style=\"text-align: center;\"\n [style.width.px]=\"column.field_size\"\n [style.minWidth.px]=\"column.field_size\"\n [resizeColumn]=\"true\"\n [columnConfig]=\"column\"\n [columnDraggable]=\"i\"\n class=\"column-header\">\n <div class=\"column-drag-handle\"></div>\n {{column.label}} {{column.symbol}}\n </th>\n </tr>\n <tr\n *ngIf=\"groupItem.type === 'row' && groupItem.group?.isExpanded\"\n class=\"row-item\"\n [attr.data-row-id]=\"groupItem.row?.entity_id\"\n [style.height.px]=\"30\"\n [style.minHeight.px]=\"30\"\n [style.maxHeight.px]=\"30\"\n >\n @if(gridStore.configuration().config.allowSelection) {\n <td class=\"checkbox-column\" style=\"text-align: center;\">\n <input\n type=\"checkbox\"\n [checked]=\"isRowSelected(groupItem.row?.entity_id)\"\n (change)=\"toggleRowSelection($event, groupItem.row)\"\n >\n </td>\n }\n <td #cell *ngFor=\"let column of columns(); trackBy: trackByColumnFn\"\n [style.width.px]=\"column.field_size\"\n [style.minWidth.px]=\"column.field_size\"\n class=\"data-cell\"\n [style.height.px]=\"30\"\n [style.minHeight.px]=\"30\"\n [style.maxHeight.px]=\"30\"\n [matTooltipClass]=\"'error-message'\"\n [matTooltip]=\"datacell.error()?'Error: ' + datacell.error():''\"\n matTooltipPosition=\"below\"\n >\n <div class=\"cell-content\">\n <data-cell\n #datacell \n [td]=cell\n [fieldSize]=\"column.field_size\"\n [columnDatatype]=\"column.datatype\"\n [columnName]=\"column.name\"\n [value]=\"groupItem.row?.[column.name] || ''\"\n [column]=\"column\"\n [mode]=\"mode()\"\n [isEditable]=\"isEditable()\"\n [drillable]=\"column.enableDrilldown || false\"\n [id]=\"groupItem.row?.['entity_id'] || '_' ||column.name\"\n [eruGridStore]=\"gridStore\"\n ></data-cell>\n </div>\n </td>\n </tr>\n\n <tr\n *ngIf=\"groupItem.type === 'ghost-loading' && groupItem.group?.isExpanded\"\n class=\"ghost-loading-row\"\n [style.height.px]=\"30\"\n [style.minHeight.px]=\"30\"\n [style.maxHeight.px]=\"30\"\n >\n <td class=\"checkbox-column\"></td>\n <td *ngFor=\"let column of columns(); trackBy: trackByColumnFn\"\n [style.width.px]=\"column.field_size\"\n [style.minWidth.px]=\"column.field_size\"\n class=\"ghost-cell-container\"\n >\n <div class=\"ghost-cell\"></div>\n </td>\n </tr>\n\n <tr\n *ngIf=\"groupItem.type === 'row-place-holder'\"\n class=\"group-separator\"\n >\n <td [attr.colspan]=\"groupSeperatorColSpan()\" class=\"separator-cell\"></td>\n </tr>\n </ng-container>\n </tbody>\n </table>\n </div>\n </cdk-virtual-scroll-viewport>\n </ng-container> \n}\n</div>\n\n <!-- Pivot Table Header Template -->\n <ng-template #pivotTableHead>\n <thead>\n @if (hasNestedHeaders()) {\n <ng-container >\n @for (headerRow of getHeaderRows(); track headerRow; let rowIndex = $index) {\n <tr class=\"pivot-header pivot-header-container\" \n [class.pivot-header-level]=\"'level-' + rowIndex\">\n @for (header of headerRow; track trackByHeaderFn($index, header); let colIndex = $index) {\n <th\n [attr.colspan]=\"header.colspan\"\n [attr.rowspan]=\"header.rowspan\"\n [resizeColumn]=\"gridStore.isFeatureEnabled('columnResizable') && header.level === 0\"\n class=\"column-header pivot-column-header nested-header\"\n [class.row-dimension-header]=\"isRowDimensionHeader(header)\"\n [class.column-dimension-header]=\"!isRowDimensionHeader(header)\"\n [class.expanded]=\"header.isExpanded\"\n [class.collapsed]=\"!header.isExpanded\"\n [class.sticky-column]=\"isStickyColumn(header.name, colIndex)\"\n [style.position]=\"isStickyColumn(header.name, colIndex) ? 'sticky' : 'static'\"\n [style.left.px]=\"getStickyColumnLeft(header.name, colIndex)\"\n [style.z-index]=\"isStickyColumn(header.name, colIndex) ? 100 : 1\"\n style=\"min-height: 40px; height: auto; padding: 8px 6px;\">\n <div class=\"header-content\">\n \n <data-cell\n [fieldSize]=\"header.field_size\"\n [columnDatatype]=\"header.dataType\"\n [columnName]=\"header.name\"\n [value]=\"header.label\"\n [column]=\"header\"\n [frozenGrandTotalCell]=\"true\"\n [drillable]=\"header.enableDrilldown || false\"\n [mode]=\"mode()\"\n [isEditable]=\"isEditable()\"\n [id]=\"'pivot_' + $index + '_' + header.name\"\n [eruGridStore]=\"gridStore\">\n </data-cell>\n <!-- <span class=\"header-label header-wrap-text\">{{header.label}}</span> -->\n <!-- <button *ngIf=\"!isRowDimensionHeader(header)\" \n class=\"collapse-toggle-btn\"\n [title]=\"header.isExpanded ? 'Collapse group' : 'Expand group'\"\n (click)=\"toggleColumnGroup(header.groupKey)\"\n type=\"button\">\n <span class=\"collapse-icon\">+</span>\n </button> -->\n </div>\n </th>\n }\n </tr>\n }\n </ng-container>\n } @else {\n <!-- Simple header fallback -->\n <ng-container>\n <tr class=\"pivot-header\" [class.freeze-header-enabled]=\"freezeHeader()\">\n @for (column of getLeafColumns(); track trackByColumnFn($index, column); let colIndex = $index) {\n <th [style.width.px]=\"column.field_size\"\n [style.minWidth.px]=\"column.field_size\"\n [resizeColumn]=\"gridStore.isFeatureEnabled('columnResizable')\"\n [columnConfig]=\"column\"\n class=\"column-header pivot-column-header\"\n [class.sticky-column]=\"isStickyColumn(column.name, colIndex)\"\n [style.position]=\"isStickyColumn(column.name, colIndex) ? 'sticky' : 'static'\"\n [style.left.px]=\"getStickyColumnLeft(column.name, colIndex)\"\n [style.z-index]=\"isStickyColumn(column.name, colIndex) ? 100 : 1\"\n style = \"min-height: 40px;height: auto;padding: 8px 6px;position: relative;left: 0px;z-index: 1\">\n {{column.label}}\n </th>\n }\n </tr>\n </ng-container>\n }\n \n </thead>\n</ng-template>\n\n<!-- Column Group Template for consistent column widths -->\n<ng-template #pivotColGroup>\n <colgroup>\n @for (column of getLeafColumns(); track trackByColumnFn($index, column)) {\n <col [style]=\"'width: ' + column.field_size + 'px !important; min-width: ' + column.field_size + 'px !important; max-width: ' + column.field_size + 'px !important; --col-width: ' + column.field_size + 'px'\">\n }\n </colgroup>\n</ng-template>\n\n<ng-template #pivotGrandTotal>\n<tbody class=\"pivot-tbody\">\n @for (pivotRow of gridStore.pivotGrandTotalData(); track trackByPivotRowFn($index, pivotRow); let i = $index) {\n <tr \n class=\"pivot-row grand-total-row\"\n [class.grand-total-bold]=\"pivotRow._isGrandTotal && grandTotalStyle() === 'bold'\"\n [class.grand-total-italic]=\"pivotRow._isGrandTotal && grandTotalStyle() === 'italic'\"\n [class.grand-total-highlighted]=\"pivotRow._isGrandTotal && grandTotalStyle() === 'highlighted'\"\n [style.height.px]=\"50\"\n [attr.data-pivot-row]=\"i\">\n <!-- <td colspan=\"20\">{{pivotRow | json}}</td> -->\n @for (column of getLeafColumns(); track trackByColumnFn($index, column); let colIndex = $index) {\n <td\n [attr.rowspan]=\"getEffectiveRowspan(i, column.name)\" \n [style.width.px]=\"column.field_size\"\n [style.minWidth.px]=\"column.field_size\"\n class=\"pivot-cell\"\n [class.row-dimension-cell]=\"isRowDimensionColumn(column.name)\"\n [class.column-dimension-cell]=\"!isRowDimensionColumn(column.name)\"\n [class.aggregated-value]=\"!isRowDimensionColumn(column.name) && column.datatype === 'number'\"\n [class.rowspan-cell]=\"getEffectiveRowspan(i, column.name) || 1 > 1\"\n [class.sticky-column]=\"isStickyColumn(column.name, colIndex)\"\n [style.position]=\"isStickyColumn(column.name, colIndex) ? 'sticky' : 'static'\"\n [style.left.px]=\"getStickyColumnLeft(column.name, colIndex)\"\n [style.z-index]=\"isStickyColumn(column.name, colIndex) ? 99 : 1\"\n [style.height.px]=\"50\"\n [attr.xx]=\"i\"> \n <div class=\"cell-content pivot-cell-content\">\n <data-cell\n [fieldSize]=\"column.field_size\"\n [columnDatatype]=\"column.datatype\"\n [columnName]=\"column.name\"\n [value]=\"getEffectiveCellValue(i,column.name, pivotRow)\"\n [column]=\"column\"\n [frozenGrandTotalCell]=\"true\"\n [drillable]=\"column.enableDrilldown || false\"\n [mode]=\"mode()\"\n [isEditable]=\"isEditable()\"\n [id]=\"'pivot_' + i + '_' + column.name\"\n [eruGridStore]=\"gridStore\">\n </data-cell>\n </div>\n </td>\n }\n </tr>\n}\n</tbody>\n</ng-template>\n", styles: ["@charset \"UTF-8\";:host{display:block;width:100%;height:100%;font-family:var(--grid-font-family);--grid-primary: #6750a4;--grid-on-primary: #ffffff;--grid-surface: #fef7ff;--grid-surface-variant: #e7e0ec;--grid-surface-container: #f3edf7;--grid-surface-container-high: #ede7f0;--grid-on-surface: #1d1b20;--grid-on-surface-variant: #49454f;--grid-outline: #79757f;--grid-outline-variant: #cac4d0;--grid-error: #ba1a1a;--grid-error-container: #ffdad6;--grid-font-family: \"Poppins\", \"Roboto\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;--grid-font-size-body: 12px;--grid-font-size-caption: 12px !important;--grid-line-height-body: 1;--grid-aggregation-text-align: right;--grid-number-text-align: right;--grid-spacing-xxs: 2px;--grid-spacing-xs: 4px;--grid-spacing-sm: 8px;--grid-spacing-md: 16px;--grid-spacing-lg: 24px;--grid-border-radius: 4px;--grid-elevation-1: 0px 1px 2px 0px rgba(0, 0, 0, .3), 0px 1px 3px 1px rgba(0, 0, 0, .15);--grid-elevation-2: 0px 1px 2px 0px rgba(0, 0, 0, .3), 0px 2px 6px 2px rgba(0, 0, 0, .15)}.incremental-row-container{padding:10px;width:100%;height:100%;min-height:var(--table-min-height, 400px);max-height:none;overflow:auto;position:relative;background-color:var(--grid-surface);border-radius:var(--grid-border-radius);box-shadow:var(--grid-elevation-1);font-family:var(--grid-font-family)}.viewport{overflow-x:auto;overflow-y:auto;background-color:var(--grid-surface);scrollbar-gutter:stable}.viewport.apply-cdk-width{width:calc(var(--table-total-width) + 10px)!important}.table-viewport{background-color:var(--grid-surface);height:auto;min-height:var(--table-min-height, 300px);max-height:80vh;overflow-x:auto;overflow-y:auto}.pivot-viewport{min-height:var(--table-min-height, 300px);overflow-x:auto;overflow-y:auto;background-color:var(--grid-surface)}.pivot-viewport .cdk-virtual-scroll-content-wrapper{width:auto;height:auto}.table-wrapper{min-width:100%;overflow-x:visible}.incremental-row-container .eru-grid-table,.eru-grid-table{width:100%!important;border-collapse:separate;border-spacing:0;table-layout:fixed!important;background-color:var(--grid-surface);color:var(--grid-on-surface);font-size:var(--grid-font-size-body);line-height:var(--grid-line-height-body)}.eru-grid-table th,.eru-grid-table td{text-align:left;overflow:hidden!important;text-overflow:ellipsis!important;white-space:nowrap!important;background-color:var(--grid-surface);color:var(--grid-on-surface);min-width:0;max-width:100%!important;box-sizing:border-box;position:relative}.eru-grid-table th:hover,.eru-grid-table td:hover{background-color:var(--grid-surface-variant)}.eru-grid-table thead{background-color:var(--grid-surface-container);transform:translateZ(0);will-change:transform;backface-visibility:hidden}.eru-grid-table thead.freeze-header-enabled{box-shadow:0 2px 4px #0000001a;border-bottom:2px solid var(--grid-outline)}.eru-grid-table thead th{background-color:var(--grid-surface-container);color:var(--grid-on-surface);font-weight:500;font-size:var(--grid-font-size-caption)}.checkbox-column{width:50px;min-width:50px;max-width:50px;text-align:center;border:1px solid var(--grid-outline);background-color:var(--grid-surface-container)}.checkbox-column input[type=checkbox]{width:16px;height:16px;cursor:pointer;accent-color:var(--grid-primary);border-radius:var(--grid-border-radius)}.checkbox-column input[type=checkbox]:focus{outline:2px solid var(--grid-primary);outline-offset:2px}.group-header{background-color:var(--grid-surface-container);color:var(--grid-on-surface);font-size:var(--grid-font-size-caption);font-weight:500;border-bottom:1px solid var(--grid-outline);cursor:pointer;transition:background-color .2s ease}.group-header:hover{background-color:var(--grid-surface-container-high)}.group-header .group-title{font-weight:600;color:var(--grid-primary)}.group-header .group-row-count{color:var(--grid-on-surface-variant);font-size:var(--grid-font-size-caption);margin-left:var(--grid-spacing-sm)}.row-item{border:1px solid var(--grid-outline);background-color:var(--grid-surface);transition:background-color .2s ease,box-shadow .2s ease}.row-item:hover{background-color:var(--grid-surface-variant);box-shadow:var(--grid-elevation-1)}.column-header{font-weight:500;text-align:center!important;font-size:var(--grid-font-size-caption, 12px);position:relative;-webkit-user-select:none;user-select:none;background-color:var(--grid-surface-container);color:var(--grid-on-surface)}.column-header:hover{background-color:var(--grid-surface-container-high)}.column-drag-handle{position:absolute;left:0;top:0;bottom:0;width:12px;cursor:grab;opacity:0;transition:opacity .2s ease,background-color .2s ease;z-index:2;display:flex;align-items:center;justify-content:center;border-right:1px solid transparent}.column-drag-handle:after{content:\"\\22ee\\22ee\";font-size:14px;color:var(--grid-on-surface-variant);transform:rotate(90deg)}.column-drag-handle:hover{background-color:var(--grid-surface-container-high);border-right-color:var(--grid-outline)}.column-header:hover .column-drag-handle{opacity:1}.column-drag-handle:active{cursor:grabbing}.dragging{opacity:1;background-color:var(--grid-surface-container);box-shadow:var(--grid-elevation-2)}.drag-over{background-color:var(--grid-surface-container);border-color:var(--grid-primary)}.data-cell{background-color:var(--grid-surface);color:var(--grid-on-surface);font-size:var(--grid-font-size-body)}.cell-content{align-items:center}.cell-content .mdc-text-field{padding:0px var(--grid-spacing-xxs)!important}.cell-display-text{align-items:center;padding:0px var(--grid-spacing-xs)}.ghost-loading-row{background-color:transparent}.ghost-cell-container{padding:var(--grid-spacing-sm)}.ghost-cell{height:20px;width:100%;background-color:var(--grid-surface-container);animation:pulse 1.5s ease-in-out infinite;border-radius:var(--grid-border-radius)}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.resizing{cursor:col-resize;-webkit-user-select:none;user-select:none}.column-resizer{position:absolute;right:0;top:0;bottom:0;width:4px;cursor:col-resize;background-color:transparent;transition:background-color .2s ease}.column-resizer:hover{background-color:var(--grid-primary)}.group-separator{height:var(--grid-spacing-sm);background-color:var(--grid-surface-variant)}.group-separator .separator-cell{background-color:var(--grid-surface-variant);border:none;height:var(--grid-spacing-sm)}.error-state{background-color:var(--grid-error-container);color:var(--grid-error);border-color:var(--grid-error)}.error-message{background-color:var(--grid-error);color:#fff;padding:var(--grid-spacing-sm);border-radius:var(--grid-border-radius);font-size:var(--grid-font-size-caption)}.incremental-row-container .eru-grid-table tbody,.incremental-row-container .eru-grid-table{position:relative}.incremental-row-container .eru-grid-table.show-column-lines{border-right:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important;border-top:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important;border-bottom:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important}.incremental-row-container .eru-grid-table:not(.show-column-lines){border:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important}.incremental-row-container .eru-grid-table thead:after{content:\"\";position:absolute;bottom:0;left:0;right:0;height:calc(var(--grid-outline-width, 1px) * 2);background-color:var(--grid-outline, #e0e0e0);pointer-events:none;z-index:10}.incremental-row-container .eru-grid-table.show-column-lines thead th{border-left:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important;border-bottom:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important}.incremental-row-container .eru-grid-table.show-column-lines tbody td{border-left:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important}.incremental-row-container .eru-grid-table.show-row-lines thead th{border-left:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important;border-bottom:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important}.incremental-row-container .eru-grid-table.show-row-lines tbody td{border-bottom:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important}@media (max-width: 768px){.incremental-row-container{height:600px}.eru-grid-table th,.eru-grid-table td{font-size:var(--grid-font-size-caption)}.checkbox-column{width:40px;min-width:40px;max-width:40px}}@media (prefers-contrast: high){.eru-grid-table th,.eru-grid-table td{border-width:2px}.row-item:hover{border-width:2px;border-color:var(--grid-primary)}}@media (prefers-reduced-motion: reduce){.row-item,.column-drag-handle,.ghost-cell{transition:none;animation:none}}.pivot-table .nested-header{text-align:center;font-weight:600;background:var(--grid-surface-container)}.pivot-table .nested-header.row-dimension-header{background:var(--grid-surface-container);font-weight:600}.pivot-table .pivot-header-leafcols{padding:0;margin:0;height:0}.pivot-table .pivot-header-level.level-0 .nested-header{font-size:14px;padding:12px 8px}.pivot-table .pivot-header-level.level-1 .nested-header{font-size:13px;padding:10px 6px}.pivot-table .pivot-header-level.level-2 .nested-header{font-size:12px;padding:8px 4px}.pivot-table .nested-header:hover{background:var(--grid-surface-variant);color:var(--grid-primary);transition:all .2s ease}.pivot-table .pivot-cell.aggregated-value{font-weight:500;font-family:Roboto Mono,monospace}.pivot-table .pivot-cell-content{display:flex;justify-content:center;align-items:center;min-height:38px}.pivot-table .rowspan-cell{vertical-align:middle}.pivot-table .rowspan-cell .pivot-cell-content{height:100%}.pivot-mode .incremental-row-container{display:flex;flex-direction:column;height:auto;max-height:85vh;overflow:auto}.pivot-mode .h-shell{position:relative;width:calc(100% - var(--scrollbar-width, 17px))!important;top:0;z-index:1;overflow-x:hidden;overflow-y:hidden;scrollbar-width:none;-ms-overflow-style:none}.pivot-mode .h-shell::-webkit-scrollbar{display:none}.pivot-mode .gt-shell{position:relative;bottom:50px;flex-shrink:0;overflow-x:hidden;overflow-y:hidden;scrollbar-width:none;-ms-overflow-style:none}.pivot-mode .gt-shell::-webkit-scrollbar{display:none}.pivot-mode .gt-shell table{border-bottom:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important}.pivot-mode .gt-shell.adjust-bottom-vs{bottom:66px!important}.pivot-mode .gt-shell.adjust-bottom:not(.adjust-bottom-vs){bottom:calc(66px - var(--scrollbar-width, 17px))!important}.pivot-mode .header-shell{flex-shrink:0;width:100%;box-sizing:border-box;padding-right:var(--scrollbar-width, 17px);overflow-x:auto;overflow-y:hidden;scrollbar-width:none;-ms-overflow-style:none}.pivot-mode .header-shell::-webkit-scrollbar{display:none}.pivot-mode .header-shell.apply-cdk-width{width:calc(var(--table-total-width) + 10px)!important}.pivot-mode .header-shell .eru-grid-table{margin-bottom:0;width:100%;table-layout:fixed}.pivot-mode .header-shell .eru-grid-table thead{background:var(--grid-surface-container)}.pivot-mode .header-shell .eru-grid-table thead th{background:var(--grid-surface-container);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:var(--col-width)!important;min-width:var(--col-width)!important;max-width:var(--col-width)!important}.pivot-mode .header-shell .eru-grid-table thead th.sticky-column{position:sticky;background:var(--grid-surface-container);z-index:111}.pivot-mode .header-shell .eru-grid-table tbody td{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:var(--col-width)!important;min-width:var(--col-width)!important;max-width:var(--col-width)!important}.pivot-mode .pivot-container{display:flex;flex-direction:column;height:100%;width:100%;overflow:hidden}.pivot-mode .pivot-table{width:auto!important;min-width:100%!important;table-layout:fixed!important;width:100%!important}.pivot-mode .pivot-table colgroup col{width:var(--col-width)!important;min-width:var(--col-width)!important;max-width:var(--col-width)!important;flex:none!important;flex-shrink:0!important;flex-grow:0!important}.pivot-mode .pivot-table td,.pivot-mode .pivot-table th{box-sizing:border-box!important;flex:none!important;flex-shrink:0!important;flex-grow:0!important;word-wrap:break-word!important;word-break:break-all!important}.pivot-mode .pivot-table *{max-width:var(--col-width)!important;box-sizing:border-box!important}.pivot-mode .pivot-table colgroup{width:100%!important}.pivot-mode .pivot-table colgroup col{width:var(--col-width)!important;min-width:var(--col-width)!important;max-width:var(--col-width)!important;flex-basis:var(--col-width)!important;flex:0 0 var(--col-width)!important}.pivot-mode .pivot-table table{width:100%!important;table-layout:fixed!important;border-collapse:collapse!important;border-spacing:0!important}.pivot-mode .pivot-table[style*=--table-total-width]{width:var(--table-total-width)!important;min-width:var(--table-total-width)!important;max-width:var(--table-total-width)!important}.pivot-mode .pivot-table colgroup col{width:var(--col-width)!important;min-width:var(--col-width)!important;max-width:var(--col-width)!important;flex:0 0 var(--col-width)!important;flex-basis:var(--col-width)!important;flex-grow:0!important;flex-shrink:0!important;overflow:hidden!important}.pivot-mode .pivot-table tbody td,.pivot-mode .pivot-table thead th{width:var(--col-width)!important;min-width:var(--col-width)!important;max-width:var(--col-width)!important;overflow:hidden!important;text-overflow:ellipsis!important;white-space:nowrap!important}.pivot-mode .pivot-table .cell-content,.pivot-mode .pivot-table data-cell{width:100%!important;max-width:100%!important;overflow:hidden!important;text-overflow:ellipsis!important;white-space:nowrap!important;display:block!important}.pivot-mode .pivot-table table{width:var(--table-total-width)!important;min-width:var(--table-total-width)!important;max-width:var(--table-total-width)!important;table-layout:fixed!important;border-collapse:collapse!important;border-spacing:0!important;word-wrap:break-word!important;word-break:break-all!important}.pivot-mode .pivot-tbody tr.pivot-row{min-height:50px!important;height:50px!important}.pivot-mode .pivot-tbody tr.pivot-row:hover{background-color:var(--grid-surface-variant)}.pivot-mode .pivot-tbody tr.pivot-row:nth-child(2n){background-color:#00000005}.pivot-mode .pivot-tbody tr.pivot-row td{min-height:50px!important;height:50px!important;vertical-align:middle;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:var(--col-width)!important;min-width:var(--col-width)!important;max-width:var(--col-width)!important}.pivot-mode .pivot-tbody tr.pivot-row td .cell-content{min-height:48px;display:flex;align-items:center;justify-content:center}.pivot-mode .pivot-tbody tr.pivot-row td .cell-content data-cell{width:100%;min-height:46px;display:flex;align-items:center;justify-content:center;overflow:hidden;flex-shrink:0}.pivot-mode .pivot-cell{vertical-align:middle;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:var(--col-width)!important;min-width:var(--col-width)!important;max-width:var(--col-width)!important}.pivot-mode .pivot-cell.aggregated-value{font-weight:500;font-family:Roboto Mono,monospace}.pivot-mode .pivot-cell .cell-content{display:flex;justify-content:center;align-items:center;min-height:40px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex-shrink:0}.pivot-mode .pivot-table .subtotal-row{background-color:var(--grid-surface-container)!important;font-weight:600}.pivot-mode .pivot-table .subtotal-row td{background-color:var(--grid-surface-container);color:var(--grid-on-surface-variant)}.pivot-mode .pivot-table .subtotal-row td:first-child{color:var(--grid-primary)}.pivot-mode .pivot-table .subtotal-row td.aggregated-value{font-weight:500;color:var(--grid-primary)}.pivot-mode .pivot-table .subtotal-row:hover{background-color:var(--grid-surface-container-high)!important}.pivot-mode .pivot-table .subtotal-row:hover td{background-color:var(--grid-surface-container-high)}.pivot-mode .pivot-table .subtotal-bold td{font-weight:600!important;font-style:normal!important}.pivot-mode .pivot-table .subtotal-bold td.aggregated-value{font-weight:600!important}.pivot-mode .pivot-table .subtotal-italic td{font-style:italic!important}.pivot-mode .pivot-table .subtotal-italic td:first-child{font-weight:600!important}.pivot-mode .pivot-table .subtotal-italic td.aggregated-value{font-style:italic!important;font-weight:500!important}.pivot-mode .pivot-table .subtotal-highlighted{background-color:var(--grid-surface-variant)!important}.pivot-mode .pivot-table .subtotal-highlighted td{background-color:var(--grid-surface-variant)!important;font-weight:700!important;font-style:normal!important;color:var(--grid-primary)!important}.pivot-mode .pivot-table .subtotal-highlighted td.aggregated-value{font-weight:500!important;color:var(--grid-primary)!important}.pivot-mode .pivot-table .subtotal-highlighted:hover,.pivot-mode .pivot-table .subtotal-highlighted:hover td{background-color:var(--grid-surface-container-high)!important}.pivot-mode .pivot-table .grand-total-row{background-color:var(--grid-surface-container-high)!important;font-weight:700;font-size:var(--grid-font-size-body)}.pivot-mode .pivot-table .grand-total-row td{background-color:var(--grid-surface-container-high)!important;color:var(--grid-on-surface)}.pivot-mode .pivot-table .grand-total-row td:first-child{font-style:normal;font-weight:800;color:var(--grid-primary)}.pivot-mode .pivot-table .grand-total-row td.aggregated-value{font-weight:500;color:var(--grid-primary);font-family:Roboto Mono,monospace}.pivot-mode .pivot-table .grand-total-row:hover,.pivot-mode .pivot-table .grand-total-row:hover td{background-color:var(--grid-surface-container-high)!important}.pivot-mode .pivot-table .grand-total-bold td{font-weight:700!important;font-style:normal!important}.pivot-mode .pivot-table .grand-total-bold td.aggregated-value{font-weight:700!important}.pivot-mode .pivot-table .grand-total-italic td,.pivot-mode .pivot-table .grand-total-italic td.aggregated-value{font-style:italic!important;font-weight:500!important}.pivot-mode .pivot-table .grand-total-highlighted{background-color:var(--grid-primary)!important;box-shadow:var(--grid-elevation-2)!important}.pivot-mode .pivot-table .grand-total-highlighted td{background-color:var(--grid-primary)!important;color:var(--grid-on-primary)!important;font-weight:500!important;font-style:normal!important}.pivot-mode .pivot-table .grand-total-highlighted td.aggregated-value{color:var(--grid-on-primary)!important;font-weight:500!important}.pivot-mode .pivot-table .grand-total-highlighted:hover,.pivot-mode .pivot-table .grand-total-highlighted:hover td{background-color:var(--grid-primary)!important}.pivot-mode .pivot-table .collapsible-header{position:relative}.pivot-mode .pivot-table .collapsible-header .header-content{display:flex;align-items:center;justify-content:space-between;gap:var(--grid-spacing-xs);padding:var(--grid-spacing-xs) var(--grid-spacing-sm)}.pivot-mode .pivot-table .collapsible-header .header-label{flex:1;font-weight:600}.pivot-mode .pivot-table .collapsible-header .collapse-toggle-btn{background:none;border:none;cursor:pointer;padding:var(--grid-spacing-xxs);margin:0;display:flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:var(--grid-border-radius);color:var(--grid-on-surface-variant);transition:all .2s ease;font-size:12px;font-weight:600}.pivot-mode .pivot-table .collapsible-header .collapse-toggle-btn:hover{background-color:var(--grid-surface-container);color:var(--grid-primary);transform:scale(1.1)}.pivot-mode .pivot-table .collapsible-header .collapse-toggle-btn:focus{outline:2px solid var(--grid-primary);outline-offset:1px}.pivot-mode .pivot-table .collapsible-header .collapse-toggle-btn .collapse-icon{display:block;line-height:1;font-family:monospace;font-size:14px}.pivot-mode .pivot-table .collapsible-header.expanded .collapse-toggle-btn .collapse-icon{color:var(--grid-primary)}.pivot-mode .pivot-table .collapsible-header.collapsed{background-color:var(--grid-surface-variant)}.pivot-mode .pivot-table .collapsible-header.collapsed .header-label{font-style:italic;color:var(--grid-on-surface-variant)}.pivot-mode .pivot-table .collapsible-header.collapsed .collapse-toggle-btn .collapse-icon{color:var(--grid-outline)}.pivot-mode .pivot-table .collapsible-header:hover{background-color:var(--grid-surface-container)}.pivot-mode .pivot-table .collapsible-header:hover .header-label{color:var(--grid-on-surface)}.pivot-mode .pivot-table .pivot-single-table{display:flex;flex-direction:column;height:100%;width:100%;overflow:hidden;min-height:var(--table-min-height)!important}.pivot-mode .pivot-table .pivot-single-table .pivot-header-container{flex-shrink:0;background:var(--grid-surface)!important;overflow-x:auto;overflow-y:hidden;min-height:100px!important;height:auto!important}.pivot-mode .pivot-table .pivot-single-table .pivot-header-container .pivot-table{width:auto;min-width:100%;height:auto!important;min-height:100px!important}.pivot-mode .pivot-table .pivot-single-table .pivot-header-container .pivot-table th{background:var(--grid-surface-container)!important;padding:8px 6px!important;white-space:nowrap;min-width:50px;min-height:40px!important;height:auto!important;position:relative;visibility:visible!important;color:var(--grid-on-surface)!important}.pivot-mode .pivot-table .pivot-single-table .pivot-header-container .pivot-table th.sticky-column{position:sticky!important;background:var(--grid-surface-container)!important;border-right:2px solid var(--grid-primary)!important;box-shadow:2px 0 4px #0000001a;z-index:101!important}.pivot-mode .pivot-table .pivot-single-table .pivot-data-container{flex:1;overflow:auto;min-height:300px!important;height:auto!important}.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-viewport{height:100%!important;width:100%!important;overflow-x:auto!important;overflow-y:auto!important}.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-table,.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-data-table{width:auto;min-width:100%;height:auto!important}.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-table td,.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-data-table td{padding:8px 6px!important;white-space:nowrap;min-width:50px;min-height:32px!important;height:auto!important;background:var(--grid-surface)!important;color:var(--grid-on-surface)!important;visibility:visible!important}.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-table td.sticky-column,.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-data-table td.sticky-column{position:sticky!important;background:var(--grid-surface-container)!important;border-right:2px solid var(--grid-primary)!important;box-shadow:2px 0 4px #0000001a;z-index:100!important}.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-table tbody tr,.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-data-table tbody tr{height:auto!important;min-height:50px!important}.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-table tbody tr.pivot-row,.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-data-table tbody tr.pivot-row{visibility:visible!important;display:table-row!important}.pivot-mode .pivot-table .collapsed-column-group{background-color:var(--grid-surface-container);border-left:3px solid var(--grid-primary)}.pivot-mode .pivot-table .collapsed-column-group:hover{background-color:var(--grid-surface-container-high)}.pivot-row.subtotal-row{background-color:var(--grid-surface-variant);font-weight:500}.pivot-row.subtotal-row.subtotal-bold{font-weight:500}.pivot-row.subtotal-row.subtotal-italic{font-style:italic}.pivot-row.subtotal-row.subtotal-highlighted{background-color:var(--grid-primary);color:var(--grid-on-primary)}.pivot-row.grand-total-row{background-color:var(--grid-surface-container);font-weight:600}.pivot-row.grand-total-row.grand-total-bold{font-weight:800}.pivot-row.grand-total-row.grand-total-italic{font-style:italic}.pivot-row.grand-total-row.grand-total-highlighted{background-color:var(--grid-primary);color:var(--grid-on-primary)}.pivot-row.first-visible-row{background-color:#6750a41a!important;position:relative}.pivot-row.first-visible-row:before{content:\"\\1f441\\fe0f First Visible\";position:absolute;top:-20px;left:0;background:var(--grid-primary);color:var(--grid-on-primary);padding:2px 6px;font-size:10px;border-radius:2px;z-index:1000}.header-wrap-text{white-space:pre-wrap;word-break:auto-phrase}\n"], dependencies: [{ kind: "component", type: DataCellComponent, selector: "data-cell", inputs: ["eruGridStore", "fieldSize", "columnDatatype", "columnName", "column", "value", "id", "frozenGrandTotalCell", "td", "drillable", "mode", "isEditable"], outputs: ["tdChange"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i1$2.ɵɵCdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i1$2.ɵɵCdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i1$2.ɵɵCdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i3$1.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: ResizeColumnDirective, selector: "[resizeColumn]", inputs: ["resizeColumn", "index", "columnConfig", "gridConfig"] }, { kind: "directive", type: ColumnDragDirective, selector: "[columnDraggable]", inputs: ["columnDraggable"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
5764
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.3", type: EruGridComponent, isStandalone: true, selector: "eru-grid", inputs: { gridConfig: "gridConfig" }, providers: [EruGridStore, EruGridService], viewQueries: [{ propertyName: "viewport", first: true, predicate: CdkVirtualScrollViewport, descendants: true }, { propertyName: "rowContainer", first: true, predicate: ["rowContainer"], descendants: true }, { propertyName: "headerScroller", first: true, predicate: ["headerScroller"], descendants: true, read: ElementRef }, { propertyName: "gtScroller", first: true, predicate: ["gtScroller"], descendants: true, read: ElementRef }, { propertyName: "vp", first: true, predicate: ["vp"], descendants: true }], ngImport: i0, template: "<!-- <div style=\"background: #f0f0f0; font-size: 12px; border-bottom: 1px solid #ccc;\">\n currentPivotScrollIndex {{currentPivotScrollIndex()}} | \n firstDataRowIndex {{firstDataRowIndex()}} | \n firstTr {{firstTr}} |\n maxDepth {{maxDepth()}}\n</div> -->\n<div class=\"incremental-row-container eru-grid\" #rowContainer \n [class.pivot-mode]=\"gridStore.isPivotMode()\"\n [class.table-mode]=\"!gridStore.isPivotMode()\">\n \n <!-- Pivot Mode Template -->\n @if (gridStore.isPivotMode()) {\n <ng-container >\n <div class=\"pivot-container\" style=\"display: flex; flex-direction: column; height: 100%;\"\n [style]=\"'--table-min-height: ' + getInitialMinHeightPx() + 'px; --table-total-width: ' + getInitialTotalWidth() + 'px'\">\n <!-- Debug info for first visible row -->\n \n \n <div class=\"pivot-single-table\" style=\"height: 100%; width: 100%; overflow: hidden; display: flex; flex-direction: column;\">\n @if (freezeHeader()) {\n <div #headerScroller class=\"header-shell\">\n <table class=\"eru-grid-table pivot-table\" \n [style]=\"'width: auto; min-width: 100%; --table-total-width: ' + getInitialTotalWidth() + 'px'\"\n [class.show-column-lines]=\"showColumnLines()\" \n [class.show-row-lines]=\"showRowLines()\">\n <!-- Column Groups for consistent width -->\n <ng-container *ngTemplateOutlet=\"pivotColGroup\"></ng-container>\n \n <ng-container *ngTemplateOutlet=\"pivotTableHead\"></ng-container>\n @if(grandTotalPosition() === 'before' && freezeGrandTotal()) {\n <ng-container *ngTemplateOutlet=\"pivotGrandTotal\"></ng-container>\n }\n </table> \n </div>\n } \n <!-- Virtual Scrolled Table Body -->\n <div>\n <cdk-virtual-scroll-viewport\n #vp\n [itemSize]=\"50\"\n class=\"viewport pivot-viewport\"\n [class.apply-cdk-width]=\"applyCdkWidth()\"\n (scrolledIndexChange)=\"onPivotScroll($event)\"\n (scroll)=\"onBodyScroll($event)\"\n style=\"overflow: auto;\">\n <table class=\"eru-grid-table pivot-table\" \n [style]=\"'width: auto; min-width: 100%; --table-total-width: ' + getInitialTotalWidth() + 'px'\"\n [class.show-column-lines]=\"showColumnLines()\" \n [class.show-row-lines]=\"showRowLines()\"\n >\n <!-- Column Groups for consistent width -->\n <ng-container *ngTemplateOutlet=\"pivotColGroup\"></ng-container>\n \n @if (!freezeHeader()) {\n <ng-container *ngTemplateOutlet=\"pivotTableHead\"></ng-container> \n }\n <!-- Table Body with Virtual Scrolling -->\n <tbody class=\"pivot-tbody\">\n \n <tr *cdkVirtualFor=\"let pivotRow of gridStore.pivotDisplayData(); \n trackBy: trackByPivotRowFn; \n let i = index\"\n class=\"pivot-row\"\n [class.subtotal-row]=\"pivotRow._isSubtotal\"\n [class.grand-total-row]=\"pivotRow._isGrandTotal\"\n [class.subtotal-bold]=\"pivotRow._isSubtotal && subTotalStyle() === 'bold'\"\n [class.subtotal-italic]=\"pivotRow._isSubtotal && subTotalStyle() === 'italic'\"\n [class.subtotal-highlighted]=\"pivotRow._isSubtotal && subTotalStyle() === 'highlighted'\"\n [class.grand-total-bold]=\"pivotRow._isGrandTotal && grandTotalStyle() === 'bold'\"\n [class.grand-total-italic]=\"pivotRow._isGrandTotal && grandTotalStyle() === 'italic'\"\n [class.grand-total-highlighted]=\"pivotRow._isGrandTotal && grandTotalStyle() === 'highlighted'\"\n [style.height.px]=\"50\"\n [attr.data-pivot-row]=\"i\">\n @if ((!pivotRow._isGrandTotal && freezeGrandTotal() ) || (!freezeGrandTotal() )) { \n @for (column of getLeafColumns(); track trackByColumnFn($index, column); let colIndex = $index) {\n @if (!shouldSkipCell(i, column.name)) {\n <td\n [attr.rowspan]=\"getEffectiveRowspan(i, column.name)\"\n [style.width.px]=\"column.field_size\"\n [style.minWidth.px]=\"column.field_size\"\n class=\"pivot-cell\"\n [class.row-dimension-cell]=\"isRowDimensionColumn(column.name)\"\n [class.column-dimension-cell]=\"!isRowDimensionColumn(column.name)\"\n [class.aggregated-value]=\"!isRowDimensionColumn(column.name) && column.datatype === 'number'\"\n [class.rowspan-cell]=\"getEffectiveRowspan(i, column.name) || 1 > 1\"\n [class.sticky-column]=\"isStickyColumn(column.name, colIndex)\"\n [style.position]=\"isStickyColumn(column.name, colIndex) ? 'sticky' : 'static'\"\n [style.left.px]=\"getStickyColumnLeft(column.name, colIndex)\"\n [style.z-index]=\"isStickyColumn(column.name, colIndex) ? 99 : 1\"\n [style.height.px]=\"50\"\n [attr.xx]=\"i\"> \n <div class=\"cell-content pivot-cell-content\">\n <data-cell\n [class.aggregation]=\"!!column.aggregationFunction\"\n [fieldSize]=\"column.field_size\"\n [columnDatatype]=\"column.datatype\"\n [columnName]=\"column.name\"\n [value]=\"getEffectiveCellValue(i,column.name, pivotRow)\"\n [column]=\"column\"\n [drillable]=\"column.enableDrilldown || false\"\n [mode]=\"mode()\"\n [isEditable]=\"isEditable()\"\n [id]=\"'pivot_' + i + '_' + column.name\"\n [eruGridStore]=\"gridStore\">\n </data-cell>\n </div>\n </td>\n }\n }\n } @else {\n <td [style.height.px]=\"50\" [attr.colspan]=\"getLeafColumns().length\"> </td>\n }\n </tr> \n </tbody>\n </table>\n </cdk-virtual-scroll-viewport>\n\n </div>\n @if (freezeGrandTotal() && grandTotalPosition() === 'after') {\n <div #gtScroller class=\"header-shell gt-shell\"\n [class.adjust-bottom]=\"!applyCdkWidth()\"\n [class.adjust-bottom-vs]=\"adjustScrollWidth()\">\n <table class=\"eru-grid-table pivot-table\" \n [style]=\"'width: auto; min-width: 100%; --table-total-width: ' + getInitialTotalWidth() + 'px'\"\n [class.show-column-lines]=\"showColumnLines()\" \n [class.show-row-lines]=\"showRowLines()\">\n <ng-container *ngTemplateOutlet=\"pivotColGroup\"></ng-container>\n \n <ng-container *ngTemplateOutlet=\"pivotGrandTotal\"></ng-container>\n \n </table> \n </div>\n }\n \n\n </div>\n </div>\n </ng-container>\n} @else {\n \n<!-- Table Mode Template -->\n <ng-container>\n <cdk-virtual-scroll-viewport\n [itemSize]=\"30\"\n class=\"viewport table-viewport\"\n (scrolledIndexChange)=\"onScroll($event)\"\n [style]=\"'--table-min-height: ' + getInitialMinHeightPx() + 'px; --table-total-width: ' + getInitialTotalWidth() + 'px'\"\n >\n <div class=\"table-wrapper\">\n \n <table class=\"eru-grid-table\" [class.show-column-lines]=\"showColumnLines()\" [class.show-column-lines]=\"showColumnLines()\">\n <thead>\n <tr>\n @if(gridStore.configuration().config.allowSelection) {\n <th class=\"checkbox-column\"></th>\n }\n <th *ngFor=\"let column of columns(); trackBy: trackByColumnFn\"\n [style.width.px]=\"column.field_size\"\n [resizeColumn]=\"gridStore.isFeatureEnabled('columnResizable')\"\n [style.minWidth.px]=\"column.field_size\"\n class=\"column-header table-column-header\">\n {{column.label}}\n </th>\n </tr>\n </thead>\n <tbody *ngIf=\"columns() as columnsList\">\n <ng-container *cdkVirtualFor=\"let groupItem of groupedRows();\n trackBy: trackByGroupItemFn;\n let i = index;\n let first=first\">\n <tr\n *ngIf=\"groupItem.type === 'header' && groups().length > 1\"\n class=\"group-header\"\n (click)=\"toggleGroupExpansion(groupItem.group?.id || '')\"\n >\n \n <td class=\"checkbox-column\" style=\"border: none;\">\n {{ groupItem.group?.isExpanded ? '\u25BC' : '\u25B6' }}\n </td>\n \n <td [attr.colspan]=\"2\" style=\"border: none;\">\n <span class=\"group-title\">\n {{ groupItem.group?.title }}\n </span>\n <span class=\"group-row-count\">\n ({{ groupItem.group?.currentLoadedRows || 0 }}/{{ groupItem.group?.totalRowCount || 0 }})\n </span>\n </td>\n </tr>\n <tr *ngIf=\"groupItem.type === 'table-header' && groups().length > 1\" style=\"background:#fafafa\">\n @if(gridStore.configuration().config.allowSelection) {\n <th class=\"checkbox-column\" style=\"text-align: center;\">\n <input\n type=\"checkbox\"\n [checked]=\"isGroupSelected(groupItem.group?.id || '')\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"toggleGroupSelection($event, groupItem.group?.id || '')\"\n >\n </th>\n }\n <th *ngFor=\"let column of columns(); trackBy: trackByColumnFn;let i =index\"\n style=\"text-align: center;\"\n [style.width.px]=\"column.field_size\"\n [style.minWidth.px]=\"column.field_size\"\n [resizeColumn]=\"true\"\n [columnConfig]=\"column\"\n [columnDraggable]=\"i\"\n class=\"column-header\">\n <div class=\"column-drag-handle\"></div>\n {{column.label}} {{column.symbol}}\n </th>\n </tr>\n <tr\n *ngIf=\"groupItem.type === 'row' && groupItem.group?.isExpanded\"\n class=\"row-item\"\n [attr.data-row-id]=\"groupItem.row?.entity_id\"\n [style.height.px]=\"30\"\n [style.minHeight.px]=\"30\"\n [style.maxHeight.px]=\"30\"\n >\n @if(gridStore.configuration().config.allowSelection) {\n <td class=\"checkbox-column\" style=\"text-align: center;\">\n <input\n type=\"checkbox\"\n [checked]=\"isRowSelected(groupItem.row?.entity_id)\"\n (change)=\"toggleRowSelection($event, groupItem.row)\"\n >\n </td>\n }\n <td #cell *ngFor=\"let column of columns(); trackBy: trackByColumnFn\"\n [style.width.px]=\"column.field_size\"\n [style.minWidth.px]=\"column.field_size\"\n class=\"data-cell\"\n [style.height.px]=\"30\"\n [style.minHeight.px]=\"30\"\n [style.maxHeight.px]=\"30\"\n [matTooltipClass]=\"'error-message'\"\n [matTooltip]=\"datacell.error()?'Error: ' + datacell.error():''\"\n matTooltipPosition=\"below\"\n >\n <div class=\"cell-content\">\n <data-cell\n #datacell \n [td]=cell\n [fieldSize]=\"column.field_size\"\n [columnDatatype]=\"column.datatype\"\n [columnName]=\"column.name\"\n [value]=\"groupItem.row?.[column.name] || ''\"\n [column]=\"column\"\n [mode]=\"mode()\"\n [isEditable]=\"isEditable()\"\n [drillable]=\"column.enableDrilldown || false\"\n [id]=\"i + '_' + column.name\"\n [eruGridStore]=\"gridStore\"\n ></data-cell>\n </div>\n </td>\n </tr>\n\n <tr\n *ngIf=\"groupItem.type === 'ghost-loading' && groupItem.group?.isExpanded\"\n class=\"ghost-loading-row\"\n [style.height.px]=\"30\"\n [style.minHeight.px]=\"30\"\n [style.maxHeight.px]=\"30\"\n >\n @if(gridStore.configuration().config.allowSelection) {\n <th class=\"checkbox-column\"></th>\n }\n <td *ngFor=\"let column of columns(); trackBy: trackByColumnFn\"\n [style.width.px]=\"column.field_size\"\n [style.minWidth.px]=\"column.field_size\"\n class=\"ghost-cell-container\"\n >\n <div class=\"ghost-cell\"></div>\n </td>\n </tr>\n\n <tr\n *ngIf=\"groupItem.type === 'row-place-holder'\"\n class=\"group-separator\"\n >\n <td [attr.colspan]=\"groupSeperatorColSpan()\" class=\"separator-cell\"></td>\n </tr>\n </ng-container>\n </tbody>\n </table>\n </div>\n </cdk-virtual-scroll-viewport>\n </ng-container> \n}\n</div>\n\n <!-- Pivot Table Header Template -->\n <ng-template #pivotTableHead>\n <thead>\n @if (hasNestedHeaders()) {\n <ng-container >\n @for (headerRow of getHeaderRows(); track headerRow; let rowIndex = $index) {\n <tr class=\"pivot-header pivot-header-container\" \n [class.pivot-header-level]=\"'level-' + rowIndex\">\n @for (header of headerRow; track trackByHeaderFn($index, header); let colIndex = $index) {\n <th\n [attr.colspan]=\"header.colspan\"\n [attr.rowspan]=\"header.rowspan\"\n [resizeColumn]=\"gridStore.isFeatureEnabled('columnResizable') && header.level === 0\"\n class=\"column-header pivot-column-header nested-header\"\n [class.row-dimension-header]=\"isRowDimensionHeader(header)\"\n [class.column-dimension-header]=\"!isRowDimensionHeader(header)\"\n [class.expanded]=\"header.isExpanded\"\n [class.collapsed]=\"!header.isExpanded\"\n [class.sticky-column]=\"isStickyColumn(header.name, colIndex)\"\n [style.position]=\"isStickyColumn(header.name, colIndex) ? 'sticky' : 'static'\"\n [style.left.px]=\"getStickyColumnLeft(header.name, colIndex)\"\n [style.z-index]=\"isStickyColumn(header.name, colIndex) ? 100 : 1\"\n style=\"min-height: 40px; height: auto; padding: 8px 6px;\">\n <div class=\"header-content\">\n \n <data-cell\n [fieldSize]=\"header.field_size\"\n [columnDatatype]=\"header.dataType\"\n [columnName]=\"header.name\"\n [value]=\"header.label\"\n [column]=\"header\"\n [frozenGrandTotalCell]=\"true\"\n [drillable]=\"header.enableDrilldown || false\"\n [mode]=\"mode()\"\n [isEditable]=\"isEditable()\"\n [id]=\"'pivot_' + $index + '_' + header.name\"\n [eruGridStore]=\"gridStore\">\n </data-cell>\n <!-- <span class=\"header-label header-wrap-text\">{{header.label}}</span> -->\n <!-- <button *ngIf=\"!isRowDimensionHeader(header)\" \n class=\"collapse-toggle-btn\"\n [title]=\"header.isExpanded ? 'Collapse group' : 'Expand group'\"\n (click)=\"toggleColumnGroup(header.groupKey)\"\n type=\"button\">\n <span class=\"collapse-icon\">+</span>\n </button> -->\n </div>\n </th>\n }\n </tr>\n }\n </ng-container>\n } @else {\n <!-- Simple header fallback -->\n <ng-container>\n <tr class=\"pivot-header\" [class.freeze-header-enabled]=\"freezeHeader()\">\n @for (column of getLeafColumns(); track trackByColumnFn($index, column); let colIndex = $index) {\n <th [style.width.px]=\"column.field_size\"\n [style.minWidth.px]=\"column.field_size\"\n [resizeColumn]=\"gridStore.isFeatureEnabled('columnResizable')\"\n [columnConfig]=\"column\"\n class=\"column-header pivot-column-header\"\n [class.sticky-column]=\"isStickyColumn(column.name, colIndex)\"\n [style.position]=\"isStickyColumn(column.name, colIndex) ? 'sticky' : 'static'\"\n [style.left.px]=\"getStickyColumnLeft(column.name, colIndex)\"\n [style.z-index]=\"isStickyColumn(column.name, colIndex) ? 100 : 1\"\n style = \"min-height: 40px;height: auto;padding: 8px 6px;position: relative;left: 0px;z-index: 1\">\n {{column.label}}\n </th>\n }\n </tr>\n </ng-container>\n }\n \n </thead>\n</ng-template>\n\n<!-- Column Group Template for consistent column widths -->\n<ng-template #pivotColGroup>\n <colgroup>\n @for (column of getLeafColumns(); track trackByColumnFn($index, column)) {\n <col [style]=\"'width: ' + column.field_size + 'px !important; min-width: ' + column.field_size + 'px !important; max-width: ' + column.field_size + 'px !important; --col-width: ' + column.field_size + 'px'\">\n }\n </colgroup>\n</ng-template>\n\n<ng-template #pivotGrandTotal>\n<tbody class=\"pivot-tbody\">\n @for (pivotRow of gridStore.pivotGrandTotalData(); track trackByPivotRowFn($index, pivotRow); let i = $index) {\n <tr \n class=\"pivot-row grand-total-row\"\n [class.grand-total-bold]=\"pivotRow._isGrandTotal && grandTotalStyle() === 'bold'\"\n [class.grand-total-italic]=\"pivotRow._isGrandTotal && grandTotalStyle() === 'italic'\"\n [class.grand-total-highlighted]=\"pivotRow._isGrandTotal && grandTotalStyle() === 'highlighted'\"\n [style.height.px]=\"50\"\n [attr.data-pivot-row]=\"i\">\n <!-- <td colspan=\"20\">{{pivotRow | json}}</td> -->\n @for (column of getLeafColumns(); track trackByColumnFn($index, column); let colIndex = $index) {\n <td\n [attr.rowspan]=\"getEffectiveRowspan(i, column.name)\" \n [style.width.px]=\"column.field_size\"\n [style.minWidth.px]=\"column.field_size\"\n class=\"pivot-cell\"\n [class.row-dimension-cell]=\"isRowDimensionColumn(column.name)\"\n [class.column-dimension-cell]=\"!isRowDimensionColumn(column.name)\"\n [class.aggregated-value]=\"!isRowDimensionColumn(column.name) && column.datatype === 'number'\"\n [class.rowspan-cell]=\"getEffectiveRowspan(i, column.name) || 1 > 1\"\n [class.sticky-column]=\"isStickyColumn(column.name, colIndex)\"\n [style.position]=\"isStickyColumn(column.name, colIndex) ? 'sticky' : 'static'\"\n [style.left.px]=\"getStickyColumnLeft(column.name, colIndex)\"\n [style.z-index]=\"isStickyColumn(column.name, colIndex) ? 99 : 1\"\n [style.height.px]=\"50\"\n [attr.xx]=\"i\"> \n <div class=\"cell-content pivot-cell-content\">\n <data-cell\n [fieldSize]=\"column.field_size\"\n [columnDatatype]=\"column.datatype\"\n [columnName]=\"column.name\"\n [value]=\"getEffectiveCellValue(i,column.name, pivotRow)\"\n [column]=\"column\"\n [frozenGrandTotalCell]=\"true\"\n [drillable]=\"column.enableDrilldown || false\"\n [mode]=\"mode()\"\n [isEditable]=\"isEditable()\"\n [id]=\"'pivot_' + i + '_' + column.name\"\n [eruGridStore]=\"gridStore\">\n </data-cell>\n </div>\n </td>\n }\n </tr>\n}\n</tbody>\n</ng-template>\n", styles: ["@charset \"UTF-8\";:host{display:block;width:100%;height:100%;font-family:var(--grid-font-family);--grid-primary: #6750a4;--grid-on-primary: #ffffff;--grid-surface: #fef7ff;--grid-surface-variant: #e7e0ec;--grid-surface-container: #f3edf7;--grid-surface-container-high: #ede7f0;--grid-on-surface: #1d1b20;--grid-on-surface-variant: #49454f;--grid-outline: #79757f;--grid-outline-variant: #cac4d0;--grid-error: #ba1a1a;--grid-error-container: #ffdad6;--grid-font-family: \"Poppins\", \"Roboto\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;--grid-font-size-body: 12px;--grid-font-size-caption: 12px !important;--grid-line-height-body: 1;--grid-aggregation-text-align: right;--grid-number-text-align: right;--grid-spacing-xxs: 2px;--grid-spacing-xs: 4px;--grid-spacing-sm: 8px;--grid-spacing-md: 16px;--grid-spacing-lg: 24px;--grid-border-radius: 4px;--grid-elevation-1: 0px 1px 2px 0px rgba(0, 0, 0, .3), 0px 1px 3px 1px rgba(0, 0, 0, .15);--grid-elevation-2: 0px 1px 2px 0px rgba(0, 0, 0, .3), 0px 2px 6px 2px rgba(0, 0, 0, .15)}.incremental-row-container{padding:10px;width:100%;height:100%;min-height:var(--table-min-height, 400px);max-height:none;overflow:auto;position:relative;background-color:var(--grid-surface);border-radius:var(--grid-border-radius);box-shadow:var(--grid-elevation-1);font-family:var(--grid-font-family)}.viewport{overflow-x:auto;overflow-y:auto;background-color:var(--grid-surface);scrollbar-gutter:stable}.viewport.apply-cdk-width{width:calc(var(--table-total-width) + 10px)!important}.table-viewport{background-color:var(--grid-surface);height:auto;min-height:var(--table-min-height, 300px);max-height:80vh;overflow-x:auto;overflow-y:auto}.pivot-viewport{min-height:var(--table-min-height, 300px);overflow-x:auto;overflow-y:auto;background-color:var(--grid-surface)}.pivot-viewport .cdk-virtual-scroll-content-wrapper{width:auto;height:auto}.table-wrapper{min-width:100%;overflow-x:visible}.incremental-row-container .eru-grid-table,.eru-grid-table{width:100%!important;border-collapse:separate;border-spacing:0;table-layout:fixed!important;background-color:var(--grid-surface);color:var(--grid-on-surface);font-size:var(--grid-font-size-body);line-height:var(--grid-line-height-body)}.eru-grid-table th,.eru-grid-table td{text-align:left;overflow:hidden!important;text-overflow:ellipsis!important;white-space:nowrap!important;background-color:var(--grid-surface);color:var(--grid-on-surface);min-width:0;max-width:100%!important;box-sizing:border-box;position:relative}.eru-grid-table th:hover,.eru-grid-table td:hover{background-color:var(--grid-surface-variant)}.eru-grid-table thead{background-color:var(--grid-surface-container);transform:translateZ(0);will-change:transform;backface-visibility:hidden}.eru-grid-table thead.freeze-header-enabled{box-shadow:0 2px 4px #0000001a;border-bottom:2px solid var(--grid-outline)}.eru-grid-table thead th{background-color:var(--grid-surface-container);color:var(--grid-on-surface);font-weight:500;font-size:var(--grid-font-size-caption)}.checkbox-column{width:50px;min-width:50px;max-width:50px;text-align:center;border:1px solid var(--grid-outline);background-color:var(--grid-surface-container)}.checkbox-column input[type=checkbox]{width:16px;height:16px;cursor:pointer;accent-color:var(--grid-primary);border-radius:var(--grid-border-radius)}.checkbox-column input[type=checkbox]:focus{outline:2px solid var(--grid-primary);outline-offset:2px}.group-header{background-color:var(--grid-surface-container);color:var(--grid-on-surface);font-size:var(--grid-font-size-caption);font-weight:500;border-bottom:1px solid var(--grid-outline);cursor:pointer;transition:background-color .2s ease}.group-header:hover{background-color:var(--grid-surface-container-high)}.group-header .group-title{font-weight:600;color:var(--grid-primary)}.group-header .group-row-count{color:var(--grid-on-surface-variant);font-size:var(--grid-font-size-caption);margin-left:var(--grid-spacing-sm)}.row-item{border:1px solid var(--grid-outline);background-color:var(--grid-surface);transition:background-color .2s ease,box-shadow .2s ease}.row-item:hover{background-color:var(--grid-surface-variant);box-shadow:var(--grid-elevation-1)}.table-column-header{padding:12px 8px}.column-header{font-weight:500;text-align:center!important;font-size:var(--grid-font-size-caption, 12px);position:relative;-webkit-user-select:none;user-select:none;background-color:var(--grid-surface-container);color:var(--grid-on-surface)}.column-header:hover{background-color:var(--grid-surface-container-high)}.column-drag-handle{position:absolute;left:0;top:0;bottom:0;width:12px;cursor:grab;opacity:0;transition:opacity .2s ease,background-color .2s ease;z-index:2;display:flex;align-items:center;justify-content:center;border-right:1px solid transparent}.column-drag-handle:after{content:\"\\22ee\\22ee\";font-size:14px;color:var(--grid-on-surface-variant);transform:rotate(90deg)}.column-drag-handle:hover{background-color:var(--grid-surface-container-high);border-right-color:var(--grid-outline)}.column-header:hover .column-drag-handle{opacity:1}.column-drag-handle:active{cursor:grabbing}.dragging{opacity:1;background-color:var(--grid-surface-container);box-shadow:var(--grid-elevation-2)}.drag-over{background-color:var(--grid-surface-container);border-color:var(--grid-primary)}.data-cell{background-color:var(--grid-surface);color:var(--grid-on-surface);font-size:var(--grid-font-size-body)}.cell-content{align-items:center}.cell-content .mdc-text-field{padding:0px var(--grid-spacing-xxs)!important}.cell-display-text{align-items:center;padding:0px var(--grid-spacing-xs)}.ghost-loading-row{background-color:transparent}.ghost-cell-container{padding:var(--grid-spacing-sm)}.ghost-cell{height:20px;width:100%;background-color:var(--grid-surface-container);animation:pulse 1.5s ease-in-out infinite;border-radius:var(--grid-border-radius)}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.resizing{cursor:col-resize;-webkit-user-select:none;user-select:none}.column-resizer{position:absolute;right:0;top:0;bottom:0;width:4px;cursor:col-resize;background-color:transparent;transition:background-color .2s ease}.column-resizer:hover{background-color:var(--grid-primary)}.group-separator{height:var(--grid-spacing-sm);background-color:var(--grid-surface-variant)}.group-separator .separator-cell{background-color:var(--grid-surface-variant);border:none;height:var(--grid-spacing-sm)}.error-state{background-color:var(--grid-error-container);color:var(--grid-error);border-color:var(--grid-error)}.error-message{background-color:var(--grid-error);color:#fff;padding:var(--grid-spacing-sm);border-radius:var(--grid-border-radius);font-size:var(--grid-font-size-caption)}.incremental-row-container .eru-grid-table tbody,.incremental-row-container .eru-grid-table{position:relative}.incremental-row-container .eru-grid-table.show-column-lines{border-right:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important;border-top:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important;border-bottom:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important}.incremental-row-container .eru-grid-table:not(.show-column-lines){border:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important}.incremental-row-container .eru-grid-table thead:after{content:\"\";position:absolute;bottom:0;left:0;right:0;height:calc(var(--grid-outline-width, 1px) * 2);background-color:var(--grid-outline, #e0e0e0);pointer-events:none;z-index:10}.incremental-row-container .eru-grid-table.show-column-lines thead th{border-left:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important;border-bottom:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important}.incremental-row-container .eru-grid-table.show-column-lines tbody td{border-left:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important}.incremental-row-container .eru-grid-table.show-row-lines thead th{border-left:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important;border-bottom:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important}.incremental-row-container .eru-grid-table.show-row-lines tbody td{border-bottom:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important}@media (max-width: 768px){.incremental-row-container{height:600px}.eru-grid-table th,.eru-grid-table td{font-size:var(--grid-font-size-caption)}.checkbox-column{width:40px;min-width:40px;max-width:40px}}@media (prefers-contrast: high){.eru-grid-table th,.eru-grid-table td{border-width:2px}.row-item:hover{border-width:2px;border-color:var(--grid-primary)}}@media (prefers-reduced-motion: reduce){.row-item,.column-drag-handle,.ghost-cell{transition:none;animation:none}}.pivot-table .nested-header{text-align:center;font-weight:600;background:var(--grid-surface-container)}.pivot-table .nested-header.row-dimension-header{background:var(--grid-surface-container);font-weight:600}.pivot-table .pivot-header-leafcols{padding:0;margin:0;height:0}.pivot-table .pivot-header-level.level-0 .nested-header{font-size:14px;padding:12px 8px}.pivot-table .pivot-header-level.level-1 .nested-header{font-size:13px;padding:10px 6px}.pivot-table .pivot-header-level.level-2 .nested-header{font-size:12px;padding:8px 4px}.pivot-table .nested-header:hover{background:var(--grid-surface-variant);color:var(--grid-primary);transition:all .2s ease}.pivot-table .pivot-cell.aggregated-value{font-weight:500;font-family:Roboto Mono,monospace}.pivot-table .pivot-cell-content{display:flex;justify-content:center;align-items:center;min-height:38px}.pivot-table .rowspan-cell{vertical-align:middle}.pivot-table .rowspan-cell .pivot-cell-content{height:100%}.pivot-mode .incremental-row-container{display:flex;flex-direction:column;height:auto;max-height:85vh;overflow:auto}.pivot-mode .h-shell{position:relative;width:calc(100% - var(--scrollbar-width, 17px))!important;top:0;z-index:1;overflow-x:hidden;overflow-y:hidden;scrollbar-width:none;-ms-overflow-style:none}.pivot-mode .h-shell::-webkit-scrollbar{display:none}.pivot-mode .gt-shell{position:relative;bottom:50px;flex-shrink:0;overflow-x:hidden;overflow-y:hidden;scrollbar-width:none;-ms-overflow-style:none}.pivot-mode .gt-shell::-webkit-scrollbar{display:none}.pivot-mode .gt-shell table{border-bottom:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important}.pivot-mode .gt-shell.adjust-bottom-vs{bottom:66px!important}.pivot-mode .gt-shell.adjust-bottom:not(.adjust-bottom-vs){bottom:calc(66px - var(--scrollbar-width, 17px))!important}.pivot-mode .header-shell{flex-shrink:0;width:100%;box-sizing:border-box;padding-right:var(--scrollbar-width, 17px);overflow-x:auto;overflow-y:hidden;scrollbar-width:none;-ms-overflow-style:none}.pivot-mode .header-shell::-webkit-scrollbar{display:none}.pivot-mode .header-shell.apply-cdk-width{width:calc(var(--table-total-width) + 10px)!important}.pivot-mode .header-shell .eru-grid-table{margin-bottom:0;width:100%;table-layout:fixed}.pivot-mode .header-shell .eru-grid-table thead{background:var(--grid-surface-container)}.pivot-mode .header-shell .eru-grid-table thead th{background:var(--grid-surface-container);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:var(--col-width)!important;min-width:var(--col-width)!important;max-width:var(--col-width)!important}.pivot-mode .header-shell .eru-grid-table thead th.sticky-column{position:sticky;background:var(--grid-surface-container);z-index:111}.pivot-mode .header-shell .eru-grid-table tbody td{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:var(--col-width)!important;min-width:var(--col-width)!important;max-width:var(--col-width)!important}.pivot-mode .pivot-container{display:flex;flex-direction:column;height:100%;width:100%;overflow:hidden}.pivot-mode .pivot-table{width:auto!important;min-width:100%!important;table-layout:fixed!important;width:100%!important}.pivot-mode .pivot-table colgroup col{width:var(--col-width)!important;min-width:var(--col-width)!important;max-width:var(--col-width)!important;flex:none!important;flex-shrink:0!important;flex-grow:0!important}.pivot-mode .pivot-table td,.pivot-mode .pivot-table th{box-sizing:border-box!important;flex:none!important;flex-shrink:0!important;flex-grow:0!important;word-wrap:break-word!important;word-break:break-all!important}.pivot-mode .pivot-table *{max-width:var(--col-width)!important;box-sizing:border-box!important}.pivot-mode .pivot-table colgroup{width:100%!important}.pivot-mode .pivot-table colgroup col{width:var(--col-width)!important;min-width:var(--col-width)!important;max-width:var(--col-width)!important;flex-basis:var(--col-width)!important;flex:0 0 var(--col-width)!important}.pivot-mode .pivot-table table{width:100%!important;table-layout:fixed!important;border-collapse:collapse!important;border-spacing:0!important}.pivot-mode .pivot-table[style*=--table-total-width]{width:var(--table-total-width)!important;min-width:var(--table-total-width)!important;max-width:var(--table-total-width)!important}.pivot-mode .pivot-table colgroup col{width:var(--col-width)!important;min-width:var(--col-width)!important;max-width:var(--col-width)!important;flex:0 0 var(--col-width)!important;flex-basis:var(--col-width)!important;flex-grow:0!important;flex-shrink:0!important;overflow:hidden!important}.pivot-mode .pivot-table tbody td,.pivot-mode .pivot-table thead th{width:var(--col-width)!important;min-width:var(--col-width)!important;max-width:var(--col-width)!important;overflow:hidden!important;text-overflow:ellipsis!important;white-space:nowrap!important}.pivot-mode .pivot-table .cell-content,.pivot-mode .pivot-table data-cell{width:100%!important;max-width:100%!important;overflow:hidden!important;text-overflow:ellipsis!important;white-space:nowrap!important;display:block!important}.pivot-mode .pivot-table table{width:var(--table-total-width)!important;min-width:var(--table-total-width)!important;max-width:var(--table-total-width)!important;table-layout:fixed!important;border-collapse:collapse!important;border-spacing:0!important;word-wrap:break-word!important;word-break:break-all!important}.pivot-mode .pivot-tbody tr.pivot-row{min-height:50px!important;height:50px!important}.pivot-mode .pivot-tbody tr.pivot-row:hover{background-color:var(--grid-surface-variant)}.pivot-mode .pivot-tbody tr.pivot-row:nth-child(2n){background-color:#00000005}.pivot-mode .pivot-tbody tr.pivot-row td{min-height:50px!important;height:50px!important;vertical-align:middle;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:var(--col-width)!important;min-width:var(--col-width)!important;max-width:var(--col-width)!important}.pivot-mode .pivot-tbody tr.pivot-row td .cell-content{min-height:48px;display:flex;align-items:center;justify-content:center}.pivot-mode .pivot-tbody tr.pivot-row td .cell-content data-cell{width:100%;min-height:46px;display:flex;align-items:center;justify-content:center;overflow:hidden;flex-shrink:0}.pivot-mode .pivot-cell{vertical-align:middle;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:var(--col-width)!important;min-width:var(--col-width)!important;max-width:var(--col-width)!important}.pivot-mode .pivot-cell.aggregated-value{font-weight:500;font-family:Roboto Mono,monospace}.pivot-mode .pivot-cell .cell-content{display:flex;justify-content:center;align-items:center;min-height:40px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex-shrink:0}.pivot-mode .pivot-table .subtotal-row{background-color:var(--grid-surface-container)!important;font-weight:600}.pivot-mode .pivot-table .subtotal-row td{background-color:var(--grid-surface-container);color:var(--grid-on-surface-variant)}.pivot-mode .pivot-table .subtotal-row td:first-child{color:var(--grid-primary)}.pivot-mode .pivot-table .subtotal-row td.aggregated-value{font-weight:500;color:var(--grid-primary)}.pivot-mode .pivot-table .subtotal-row:hover{background-color:var(--grid-surface-container-high)!important}.pivot-mode .pivot-table .subtotal-row:hover td{background-color:var(--grid-surface-container-high)}.pivot-mode .pivot-table .subtotal-bold td{font-weight:600!important;font-style:normal!important}.pivot-mode .pivot-table .subtotal-bold td.aggregated-value{font-weight:600!important}.pivot-mode .pivot-table .subtotal-italic td{font-style:italic!important}.pivot-mode .pivot-table .subtotal-italic td:first-child{font-weight:600!important}.pivot-mode .pivot-table .subtotal-italic td.aggregated-value{font-style:italic!important;font-weight:500!important}.pivot-mode .pivot-table .subtotal-highlighted{background-color:var(--grid-surface-variant)!important}.pivot-mode .pivot-table .subtotal-highlighted td{background-color:var(--grid-surface-variant)!important;font-weight:700!important;font-style:normal!important;color:var(--grid-primary)!important}.pivot-mode .pivot-table .subtotal-highlighted td.aggregated-value{font-weight:500!important;color:var(--grid-primary)!important}.pivot-mode .pivot-table .subtotal-highlighted:hover,.pivot-mode .pivot-table .subtotal-highlighted:hover td{background-color:var(--grid-surface-container-high)!important}.pivot-mode .pivot-table .grand-total-row{background-color:var(--grid-surface-container-high)!important;font-weight:700;font-size:var(--grid-font-size-body)}.pivot-mode .pivot-table .grand-total-row td{background-color:var(--grid-surface-container-high)!important;color:var(--grid-on-surface)}.pivot-mode .pivot-table .grand-total-row td:first-child{font-style:normal;font-weight:800;color:var(--grid-primary)}.pivot-mode .pivot-table .grand-total-row td.aggregated-value{font-weight:500;color:var(--grid-primary);font-family:Roboto Mono,monospace}.pivot-mode .pivot-table .grand-total-row:hover,.pivot-mode .pivot-table .grand-total-row:hover td{background-color:var(--grid-surface-container-high)!important}.pivot-mode .pivot-table .grand-total-bold td{font-weight:700!important;font-style:normal!important}.pivot-mode .pivot-table .grand-total-bold td.aggregated-value{font-weight:700!important}.pivot-mode .pivot-table .grand-total-italic td,.pivot-mode .pivot-table .grand-total-italic td.aggregated-value{font-style:italic!important;font-weight:500!important}.pivot-mode .pivot-table .grand-total-highlighted{background-color:var(--grid-primary)!important;box-shadow:var(--grid-elevation-2)!important}.pivot-mode .pivot-table .grand-total-highlighted td{background-color:var(--grid-primary)!important;color:var(--grid-on-primary)!important;font-weight:500!important;font-style:normal!important}.pivot-mode .pivot-table .grand-total-highlighted td.aggregated-value{color:var(--grid-on-primary)!important;font-weight:500!important}.pivot-mode .pivot-table .grand-total-highlighted:hover,.pivot-mode .pivot-table .grand-total-highlighted:hover td{background-color:var(--grid-primary)!important}.pivot-mode .pivot-table .collapsible-header{position:relative}.pivot-mode .pivot-table .collapsible-header .header-content{display:flex;align-items:center;justify-content:space-between;gap:var(--grid-spacing-xs);padding:var(--grid-spacing-xs) var(--grid-spacing-sm)}.pivot-mode .pivot-table .collapsible-header .header-label{flex:1;font-weight:600}.pivot-mode .pivot-table .collapsible-header .collapse-toggle-btn{background:none;border:none;cursor:pointer;padding:var(--grid-spacing-xxs);margin:0;display:flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:var(--grid-border-radius);color:var(--grid-on-surface-variant);transition:all .2s ease;font-size:12px;font-weight:600}.pivot-mode .pivot-table .collapsible-header .collapse-toggle-btn:hover{background-color:var(--grid-surface-container);color:var(--grid-primary);transform:scale(1.1)}.pivot-mode .pivot-table .collapsible-header .collapse-toggle-btn:focus{outline:2px solid var(--grid-primary);outline-offset:1px}.pivot-mode .pivot-table .collapsible-header .collapse-toggle-btn .collapse-icon{display:block;line-height:1;font-family:monospace;font-size:14px}.pivot-mode .pivot-table .collapsible-header.expanded .collapse-toggle-btn .collapse-icon{color:var(--grid-primary)}.pivot-mode .pivot-table .collapsible-header.collapsed{background-color:var(--grid-surface-variant)}.pivot-mode .pivot-table .collapsible-header.collapsed .header-label{font-style:italic;color:var(--grid-on-surface-variant)}.pivot-mode .pivot-table .collapsible-header.collapsed .collapse-toggle-btn .collapse-icon{color:var(--grid-outline)}.pivot-mode .pivot-table .collapsible-header:hover{background-color:var(--grid-surface-container)}.pivot-mode .pivot-table .collapsible-header:hover .header-label{color:var(--grid-on-surface)}.pivot-mode .pivot-table .pivot-single-table{display:flex;flex-direction:column;height:100%;width:100%;overflow:hidden;min-height:var(--table-min-height)!important}.pivot-mode .pivot-table .pivot-single-table .pivot-header-container{flex-shrink:0;background:var(--grid-surface)!important;overflow-x:auto;overflow-y:hidden;min-height:100px!important;height:auto!important}.pivot-mode .pivot-table .pivot-single-table .pivot-header-container .pivot-table{width:auto;min-width:100%;height:auto!important;min-height:100px!important}.pivot-mode .pivot-table .pivot-single-table .pivot-header-container .pivot-table th{background:var(--grid-surface-container)!important;padding:8px 6px!important;white-space:nowrap;min-width:50px;min-height:40px!important;height:auto!important;position:relative;visibility:visible!important;color:var(--grid-on-surface)!important}.pivot-mode .pivot-table .pivot-single-table .pivot-header-container .pivot-table th.sticky-column{position:sticky!important;background:var(--grid-surface-container)!important;border-right:2px solid var(--grid-primary)!important;box-shadow:2px 0 4px #0000001a;z-index:101!important}.pivot-mode .pivot-table .pivot-single-table .pivot-data-container{flex:1;overflow:auto;min-height:300px!important;height:auto!important}.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-viewport{height:100%!important;width:100%!important;overflow-x:auto!important;overflow-y:auto!important}.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-table,.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-data-table{width:auto;min-width:100%;height:auto!important}.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-table td,.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-data-table td{padding:8px 6px!important;white-space:nowrap;min-width:50px;min-height:32px!important;height:auto!important;background:var(--grid-surface)!important;color:var(--grid-on-surface)!important;visibility:visible!important}.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-table td.sticky-column,.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-data-table td.sticky-column{position:sticky!important;background:var(--grid-surface-container)!important;border-right:2px solid var(--grid-primary)!important;box-shadow:2px 0 4px #0000001a;z-index:100!important}.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-table tbody tr,.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-data-table tbody tr{height:auto!important;min-height:50px!important}.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-table tbody tr.pivot-row,.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-data-table tbody tr.pivot-row{visibility:visible!important;display:table-row!important}.pivot-mode .pivot-table .collapsed-column-group{background-color:var(--grid-surface-container);border-left:3px solid var(--grid-primary)}.pivot-mode .pivot-table .collapsed-column-group:hover{background-color:var(--grid-surface-container-high)}.pivot-row.subtotal-row{background-color:var(--grid-surface-variant);font-weight:500}.pivot-row.subtotal-row.subtotal-bold{font-weight:500}.pivot-row.subtotal-row.subtotal-italic{font-style:italic}.pivot-row.subtotal-row.subtotal-highlighted{background-color:var(--grid-primary);color:var(--grid-on-primary)}.pivot-row.grand-total-row{background-color:var(--grid-surface-container);font-weight:600}.pivot-row.grand-total-row.grand-total-bold{font-weight:800}.pivot-row.grand-total-row.grand-total-italic{font-style:italic}.pivot-row.grand-total-row.grand-total-highlighted{background-color:var(--grid-primary);color:var(--grid-on-primary)}.pivot-row.first-visible-row{background-color:#6750a41a!important;position:relative}.pivot-row.first-visible-row:before{content:\"\\1f441\\fe0f First Visible\";position:absolute;top:-20px;left:0;background:var(--grid-primary);color:var(--grid-on-primary);padding:2px 6px;font-size:10px;border-radius:2px;z-index:1000}.header-wrap-text{white-space:pre-wrap;word-break:auto-phrase}\n"], dependencies: [{ kind: "component", type: DataCellComponent, selector: "data-cell", inputs: ["eruGridStore", "fieldSize", "columnDatatype", "columnName", "column", "value", "id", "frozenGrandTotalCell", "td", "drillable", "mode", "isEditable"], outputs: ["tdChange"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i1$2.ɵɵCdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i1$2.ɵɵCdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i1$2.ɵɵCdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i3$1.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: ResizeColumnDirective, selector: "[resizeColumn]", inputs: ["resizeColumn", "index", "columnConfig", "gridConfig"] }, { kind: "directive", type: ColumnDragDirective, selector: "[columnDraggable]", inputs: ["columnDraggable"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
6428
5765
  }
6429
5766
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: EruGridComponent, decorators: [{
6430
5767
  type: Component,
@@ -6435,7 +5772,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImpor
6435
5772
  MatTooltipModule,
6436
5773
  ResizeColumnDirective,
6437
5774
  ColumnDragDirective
6438
- ], template: "<!-- <div style=\"background: #f0f0f0; font-size: 12px; border-bottom: 1px solid #ccc;\">\n currentPivotScrollIndex {{currentPivotScrollIndex()}} | \n firstDataRowIndex {{firstDataRowIndex()}} | \n firstTr {{firstTr}} |\n maxDepth {{maxDepth()}}\n</div> -->\n<div class=\"incremental-row-container eru-grid\" #rowContainer \n [class.pivot-mode]=\"gridStore.isPivotMode()\"\n [class.table-mode]=\"!gridStore.isPivotMode()\">\n \n <!-- Pivot Mode Template -->\n @if (gridStore.isPivotMode()) {\n <ng-container >\n <div class=\"pivot-container\" style=\"display: flex; flex-direction: column; height: 100%;\"\n [style]=\"'--table-min-height: ' + getInitialMinHeightPx() + 'px; --table-total-width: ' + getInitialTotalWidth() + 'px'\">\n <!-- Debug info for first visible row -->\n \n \n <div class=\"pivot-single-table\" style=\"height: 100%; width: 100%; overflow: hidden; display: flex; flex-direction: column;\">\n @if (freezeHeader()) {\n <div #headerScroller class=\"header-shell\">\n <table class=\"eru-grid-table pivot-table\" \n [style]=\"'width: auto; min-width: 100%; --table-total-width: ' + getInitialTotalWidth() + 'px'\"\n [class.show-column-lines]=\"showColumnLines()\" \n [class.show-row-lines]=\"showRowLines()\">\n <!-- Column Groups for consistent width -->\n <ng-container *ngTemplateOutlet=\"pivotColGroup\"></ng-container>\n \n <ng-container *ngTemplateOutlet=\"pivotTableHead\"></ng-container>\n @if(grandTotalPosition() === 'before' && freezeGrandTotal()) {\n <ng-container *ngTemplateOutlet=\"pivotGrandTotal\"></ng-container>\n }\n </table> \n </div>\n } \n <!-- Virtual Scrolled Table Body -->\n <div>\n <cdk-virtual-scroll-viewport\n #vp\n [itemSize]=\"50\"\n class=\"viewport pivot-viewport\"\n [class.apply-cdk-width]=\"applyCdkWidth()\"\n (scrolledIndexChange)=\"onPivotScroll($event)\"\n (scroll)=\"onBodyScroll($event)\"\n style=\"overflow: auto;\">\n <table class=\"eru-grid-table pivot-table\" \n [style]=\"'width: auto; min-width: 100%; --table-total-width: ' + getInitialTotalWidth() + 'px'\"\n [class.show-column-lines]=\"showColumnLines()\" \n [class.show-row-lines]=\"showRowLines()\"\n >\n <!-- Column Groups for consistent width -->\n <ng-container *ngTemplateOutlet=\"pivotColGroup\"></ng-container>\n \n @if (!freezeHeader()) {\n <ng-container *ngTemplateOutlet=\"pivotTableHead\"></ng-container> \n }\n <!-- Table Body with Virtual Scrolling -->\n <tbody class=\"pivot-tbody\">\n \n <tr *cdkVirtualFor=\"let pivotRow of gridStore.pivotDisplayData(); \n trackBy: trackByPivotRowFn; \n let i = index\"\n class=\"pivot-row\"\n [class.subtotal-row]=\"pivotRow._isSubtotal\"\n [class.grand-total-row]=\"pivotRow._isGrandTotal\"\n [class.subtotal-bold]=\"pivotRow._isSubtotal && subTotalStyle() === 'bold'\"\n [class.subtotal-italic]=\"pivotRow._isSubtotal && subTotalStyle() === 'italic'\"\n [class.subtotal-highlighted]=\"pivotRow._isSubtotal && subTotalStyle() === 'highlighted'\"\n [class.grand-total-bold]=\"pivotRow._isGrandTotal && grandTotalStyle() === 'bold'\"\n [class.grand-total-italic]=\"pivotRow._isGrandTotal && grandTotalStyle() === 'italic'\"\n [class.grand-total-highlighted]=\"pivotRow._isGrandTotal && grandTotalStyle() === 'highlighted'\"\n [style.height.px]=\"50\"\n [attr.data-pivot-row]=\"i\">\n @if ((!pivotRow._isGrandTotal && freezeGrandTotal() ) || (!freezeGrandTotal() )) { \n @for (column of getLeafColumns(); track trackByColumnFn($index, column); let colIndex = $index) {\n @if (!shouldSkipCell(i, column.name)) {\n <td\n [attr.rowspan]=\"getEffectiveRowspan(i, column.name)\"\n [style.width.px]=\"column.field_size\"\n [style.minWidth.px]=\"column.field_size\"\n class=\"pivot-cell\"\n [class.row-dimension-cell]=\"isRowDimensionColumn(column.name)\"\n [class.column-dimension-cell]=\"!isRowDimensionColumn(column.name)\"\n [class.aggregated-value]=\"!isRowDimensionColumn(column.name) && column.datatype === 'number'\"\n [class.rowspan-cell]=\"getEffectiveRowspan(i, column.name) || 1 > 1\"\n [class.sticky-column]=\"isStickyColumn(column.name, colIndex)\"\n [style.position]=\"isStickyColumn(column.name, colIndex) ? 'sticky' : 'static'\"\n [style.left.px]=\"getStickyColumnLeft(column.name, colIndex)\"\n [style.z-index]=\"isStickyColumn(column.name, colIndex) ? 99 : 1\"\n [style.height.px]=\"50\"\n [attr.xx]=\"i\"> \n <div class=\"cell-content pivot-cell-content\">\n <data-cell\n [class.aggregation]=\"!!column.aggregationFunction\"\n [fieldSize]=\"column.field_size\"\n [columnDatatype]=\"column.datatype\"\n [columnName]=\"column.name\"\n [value]=\"getEffectiveCellValue(i,column.name, pivotRow)\"\n [column]=\"column\"\n [drillable]=\"column.enableDrilldown || false\"\n [mode]=\"mode()\"\n [isEditable]=\"isEditable()\"\n [id]=\"'pivot_' + i + '_' + column.name\"\n [eruGridStore]=\"gridStore\">\n </data-cell>\n </div>\n </td>\n }\n }\n } @else {\n <td [style.height.px]=\"50\" [attr.colspan]=\"getLeafColumns().length\"> </td>\n }\n </tr> \n </tbody>\n </table>\n </cdk-virtual-scroll-viewport>\n\n </div>\n @if (freezeGrandTotal() && grandTotalPosition() === 'after') {\n <div #gtScroller class=\"header-shell gt-shell\"\n [class.adjust-bottom]=\"!applyCdkWidth()\"\n [class.adjust-bottom-vs]=\"adjustScrollWidth()\">\n <table class=\"eru-grid-table pivot-table\" \n [style]=\"'width: auto; min-width: 100%; --table-total-width: ' + getInitialTotalWidth() + 'px'\"\n [class.show-column-lines]=\"showColumnLines()\" \n [class.show-row-lines]=\"showRowLines()\">\n <ng-container *ngTemplateOutlet=\"pivotColGroup\"></ng-container>\n \n <ng-container *ngTemplateOutlet=\"pivotGrandTotal\"></ng-container>\n \n </table> \n </div>\n }\n \n\n </div>\n </div>\n </ng-container>\n} @else {\n \n<!-- Table Mode Template -->\n <ng-container>\n <cdk-virtual-scroll-viewport\n [itemSize]=\"30\"\n class=\"viewport table-viewport\"\n (scrolledIndexChange)=\"onScroll($event)\"\n >\n <div class=\"table-wrapper\">\n \n <table class=\"eru-grid-table\" [class.show-column-lines]=\"showColumnLines()\" [class.show-column-lines]=\"showColumnLines()\">\n <thead>\n <tr>\n @if(gridStore.configuration().config.allowSelection) {\n <th class=\"checkbox-column\"></th>\n }\n <th *ngFor=\"let column of columns(); trackBy: trackByColumnFn\"\n [style.width.px]=\"column.field_size\"\n [resizeColumn]=\"gridStore.isFeatureEnabled('columnResizable')\"\n [style.minWidth.px]=\"column.field_size\"\n class=\"column-header\">\n {{column.label}}\n </th>\n </tr>\n </thead>\n <tbody *ngIf=\"columns() as columnsList\">\n <ng-container *cdkVirtualFor=\"let groupItem of groupedRows();\n trackBy: trackByGroupItemFn;\n let i = index;\n let first=first\">\n <tr\n *ngIf=\"groupItem.type === 'header' && groups().length > 1\"\n class=\"group-header\"\n (click)=\"toggleGroupExpansion(groupItem.group?.id || '')\"\n >\n \n <td class=\"checkbox-column\" style=\"border: none;\">\n {{ groupItem.group?.isExpanded ? '\u25BC' : '\u25B6' }}\n </td>\n \n <td [attr.colspan]=\"2\" style=\"border: none;\">\n <span class=\"group-title\">\n {{ groupItem.group?.title }}\n </span>\n <span class=\"group-row-count\">\n ({{ groupItem.group?.currentLoadedRows || 0 }}/{{ groupItem.group?.totalRowCount || 0 }})\n </span>\n </td>\n </tr>\n <tr *ngIf=\"groupItem.type === 'table-header' && groups().length > 1\" style=\"background:#fafafa\">\n @if(gridStore.configuration().config.allowSelection) {\n <th class=\"checkbox-column\" style=\"text-align: center;\">\n <input\n type=\"checkbox\"\n [checked]=\"isGroupSelected(groupItem.group?.id || '')\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"toggleGroupSelection($event, groupItem.group?.id || '')\"\n >\n </th>\n }\n <th *ngFor=\"let column of columns(); trackBy: trackByColumnFn;let i =index\"\n style=\"text-align: center;\"\n [style.width.px]=\"column.field_size\"\n [style.minWidth.px]=\"column.field_size\"\n [resizeColumn]=\"true\"\n [columnConfig]=\"column\"\n [columnDraggable]=\"i\"\n class=\"column-header\">\n <div class=\"column-drag-handle\"></div>\n {{column.label}} {{column.symbol}}\n </th>\n </tr>\n <tr\n *ngIf=\"groupItem.type === 'row' && groupItem.group?.isExpanded\"\n class=\"row-item\"\n [attr.data-row-id]=\"groupItem.row?.entity_id\"\n [style.height.px]=\"30\"\n [style.minHeight.px]=\"30\"\n [style.maxHeight.px]=\"30\"\n >\n @if(gridStore.configuration().config.allowSelection) {\n <td class=\"checkbox-column\" style=\"text-align: center;\">\n <input\n type=\"checkbox\"\n [checked]=\"isRowSelected(groupItem.row?.entity_id)\"\n (change)=\"toggleRowSelection($event, groupItem.row)\"\n >\n </td>\n }\n <td #cell *ngFor=\"let column of columns(); trackBy: trackByColumnFn\"\n [style.width.px]=\"column.field_size\"\n [style.minWidth.px]=\"column.field_size\"\n class=\"data-cell\"\n [style.height.px]=\"30\"\n [style.minHeight.px]=\"30\"\n [style.maxHeight.px]=\"30\"\n [matTooltipClass]=\"'error-message'\"\n [matTooltip]=\"datacell.error()?'Error: ' + datacell.error():''\"\n matTooltipPosition=\"below\"\n >\n <div class=\"cell-content\">\n <data-cell\n #datacell \n [td]=cell\n [fieldSize]=\"column.field_size\"\n [columnDatatype]=\"column.datatype\"\n [columnName]=\"column.name\"\n [value]=\"groupItem.row?.[column.name] || ''\"\n [column]=\"column\"\n [mode]=\"mode()\"\n [isEditable]=\"isEditable()\"\n [drillable]=\"column.enableDrilldown || false\"\n [id]=\"groupItem.row?.['entity_id'] || '_' ||column.name\"\n [eruGridStore]=\"gridStore\"\n ></data-cell>\n </div>\n </td>\n </tr>\n\n <tr\n *ngIf=\"groupItem.type === 'ghost-loading' && groupItem.group?.isExpanded\"\n class=\"ghost-loading-row\"\n [style.height.px]=\"30\"\n [style.minHeight.px]=\"30\"\n [style.maxHeight.px]=\"30\"\n >\n <td class=\"checkbox-column\"></td>\n <td *ngFor=\"let column of columns(); trackBy: trackByColumnFn\"\n [style.width.px]=\"column.field_size\"\n [style.minWidth.px]=\"column.field_size\"\n class=\"ghost-cell-container\"\n >\n <div class=\"ghost-cell\"></div>\n </td>\n </tr>\n\n <tr\n *ngIf=\"groupItem.type === 'row-place-holder'\"\n class=\"group-separator\"\n >\n <td [attr.colspan]=\"groupSeperatorColSpan()\" class=\"separator-cell\"></td>\n </tr>\n </ng-container>\n </tbody>\n </table>\n </div>\n </cdk-virtual-scroll-viewport>\n </ng-container> \n}\n</div>\n\n <!-- Pivot Table Header Template -->\n <ng-template #pivotTableHead>\n <thead>\n @if (hasNestedHeaders()) {\n <ng-container >\n @for (headerRow of getHeaderRows(); track headerRow; let rowIndex = $index) {\n <tr class=\"pivot-header pivot-header-container\" \n [class.pivot-header-level]=\"'level-' + rowIndex\">\n @for (header of headerRow; track trackByHeaderFn($index, header); let colIndex = $index) {\n <th\n [attr.colspan]=\"header.colspan\"\n [attr.rowspan]=\"header.rowspan\"\n [resizeColumn]=\"gridStore.isFeatureEnabled('columnResizable') && header.level === 0\"\n class=\"column-header pivot-column-header nested-header\"\n [class.row-dimension-header]=\"isRowDimensionHeader(header)\"\n [class.column-dimension-header]=\"!isRowDimensionHeader(header)\"\n [class.expanded]=\"header.isExpanded\"\n [class.collapsed]=\"!header.isExpanded\"\n [class.sticky-column]=\"isStickyColumn(header.name, colIndex)\"\n [style.position]=\"isStickyColumn(header.name, colIndex) ? 'sticky' : 'static'\"\n [style.left.px]=\"getStickyColumnLeft(header.name, colIndex)\"\n [style.z-index]=\"isStickyColumn(header.name, colIndex) ? 100 : 1\"\n style=\"min-height: 40px; height: auto; padding: 8px 6px;\">\n <div class=\"header-content\">\n \n <data-cell\n [fieldSize]=\"header.field_size\"\n [columnDatatype]=\"header.dataType\"\n [columnName]=\"header.name\"\n [value]=\"header.label\"\n [column]=\"header\"\n [frozenGrandTotalCell]=\"true\"\n [drillable]=\"header.enableDrilldown || false\"\n [mode]=\"mode()\"\n [isEditable]=\"isEditable()\"\n [id]=\"'pivot_' + $index + '_' + header.name\"\n [eruGridStore]=\"gridStore\">\n </data-cell>\n <!-- <span class=\"header-label header-wrap-text\">{{header.label}}</span> -->\n <!-- <button *ngIf=\"!isRowDimensionHeader(header)\" \n class=\"collapse-toggle-btn\"\n [title]=\"header.isExpanded ? 'Collapse group' : 'Expand group'\"\n (click)=\"toggleColumnGroup(header.groupKey)\"\n type=\"button\">\n <span class=\"collapse-icon\">+</span>\n </button> -->\n </div>\n </th>\n }\n </tr>\n }\n </ng-container>\n } @else {\n <!-- Simple header fallback -->\n <ng-container>\n <tr class=\"pivot-header\" [class.freeze-header-enabled]=\"freezeHeader()\">\n @for (column of getLeafColumns(); track trackByColumnFn($index, column); let colIndex = $index) {\n <th [style.width.px]=\"column.field_size\"\n [style.minWidth.px]=\"column.field_size\"\n [resizeColumn]=\"gridStore.isFeatureEnabled('columnResizable')\"\n [columnConfig]=\"column\"\n class=\"column-header pivot-column-header\"\n [class.sticky-column]=\"isStickyColumn(column.name, colIndex)\"\n [style.position]=\"isStickyColumn(column.name, colIndex) ? 'sticky' : 'static'\"\n [style.left.px]=\"getStickyColumnLeft(column.name, colIndex)\"\n [style.z-index]=\"isStickyColumn(column.name, colIndex) ? 100 : 1\"\n style = \"min-height: 40px;height: auto;padding: 8px 6px;position: relative;left: 0px;z-index: 1\">\n {{column.label}}\n </th>\n }\n </tr>\n </ng-container>\n }\n \n </thead>\n</ng-template>\n\n<!-- Column Group Template for consistent column widths -->\n<ng-template #pivotColGroup>\n <colgroup>\n @for (column of getLeafColumns(); track trackByColumnFn($index, column)) {\n <col [style]=\"'width: ' + column.field_size + 'px !important; min-width: ' + column.field_size + 'px !important; max-width: ' + column.field_size + 'px !important; --col-width: ' + column.field_size + 'px'\">\n }\n </colgroup>\n</ng-template>\n\n<ng-template #pivotGrandTotal>\n<tbody class=\"pivot-tbody\">\n @for (pivotRow of gridStore.pivotGrandTotalData(); track trackByPivotRowFn($index, pivotRow); let i = $index) {\n <tr \n class=\"pivot-row grand-total-row\"\n [class.grand-total-bold]=\"pivotRow._isGrandTotal && grandTotalStyle() === 'bold'\"\n [class.grand-total-italic]=\"pivotRow._isGrandTotal && grandTotalStyle() === 'italic'\"\n [class.grand-total-highlighted]=\"pivotRow._isGrandTotal && grandTotalStyle() === 'highlighted'\"\n [style.height.px]=\"50\"\n [attr.data-pivot-row]=\"i\">\n <!-- <td colspan=\"20\">{{pivotRow | json}}</td> -->\n @for (column of getLeafColumns(); track trackByColumnFn($index, column); let colIndex = $index) {\n <td\n [attr.rowspan]=\"getEffectiveRowspan(i, column.name)\" \n [style.width.px]=\"column.field_size\"\n [style.minWidth.px]=\"column.field_size\"\n class=\"pivot-cell\"\n [class.row-dimension-cell]=\"isRowDimensionColumn(column.name)\"\n [class.column-dimension-cell]=\"!isRowDimensionColumn(column.name)\"\n [class.aggregated-value]=\"!isRowDimensionColumn(column.name) && column.datatype === 'number'\"\n [class.rowspan-cell]=\"getEffectiveRowspan(i, column.name) || 1 > 1\"\n [class.sticky-column]=\"isStickyColumn(column.name, colIndex)\"\n [style.position]=\"isStickyColumn(column.name, colIndex) ? 'sticky' : 'static'\"\n [style.left.px]=\"getStickyColumnLeft(column.name, colIndex)\"\n [style.z-index]=\"isStickyColumn(column.name, colIndex) ? 99 : 1\"\n [style.height.px]=\"50\"\n [attr.xx]=\"i\"> \n <div class=\"cell-content pivot-cell-content\">\n <data-cell\n [fieldSize]=\"column.field_size\"\n [columnDatatype]=\"column.datatype\"\n [columnName]=\"column.name\"\n [value]=\"getEffectiveCellValue(i,column.name, pivotRow)\"\n [column]=\"column\"\n [frozenGrandTotalCell]=\"true\"\n [drillable]=\"column.enableDrilldown || false\"\n [mode]=\"mode()\"\n [isEditable]=\"isEditable()\"\n [id]=\"'pivot_' + i + '_' + column.name\"\n [eruGridStore]=\"gridStore\">\n </data-cell>\n </div>\n </td>\n }\n </tr>\n}\n</tbody>\n</ng-template>\n", styles: ["@charset \"UTF-8\";:host{display:block;width:100%;height:100%;font-family:var(--grid-font-family);--grid-primary: #6750a4;--grid-on-primary: #ffffff;--grid-surface: #fef7ff;--grid-surface-variant: #e7e0ec;--grid-surface-container: #f3edf7;--grid-surface-container-high: #ede7f0;--grid-on-surface: #1d1b20;--grid-on-surface-variant: #49454f;--grid-outline: #79757f;--grid-outline-variant: #cac4d0;--grid-error: #ba1a1a;--grid-error-container: #ffdad6;--grid-font-family: \"Poppins\", \"Roboto\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;--grid-font-size-body: 12px;--grid-font-size-caption: 12px !important;--grid-line-height-body: 1;--grid-aggregation-text-align: right;--grid-number-text-align: right;--grid-spacing-xxs: 2px;--grid-spacing-xs: 4px;--grid-spacing-sm: 8px;--grid-spacing-md: 16px;--grid-spacing-lg: 24px;--grid-border-radius: 4px;--grid-elevation-1: 0px 1px 2px 0px rgba(0, 0, 0, .3), 0px 1px 3px 1px rgba(0, 0, 0, .15);--grid-elevation-2: 0px 1px 2px 0px rgba(0, 0, 0, .3), 0px 2px 6px 2px rgba(0, 0, 0, .15)}.incremental-row-container{padding:10px;width:100%;height:100%;min-height:var(--table-min-height, 400px);max-height:none;overflow:auto;position:relative;background-color:var(--grid-surface);border-radius:var(--grid-border-radius);box-shadow:var(--grid-elevation-1);font-family:var(--grid-font-family)}.viewport{overflow-x:auto;overflow-y:auto;background-color:var(--grid-surface);scrollbar-gutter:stable}.viewport.apply-cdk-width{width:calc(var(--table-total-width) + 10px)!important}.table-viewport{background-color:var(--grid-surface);height:auto;min-height:var(--table-min-height, 300px);max-height:80vh;overflow-x:auto;overflow-y:auto}.pivot-viewport{min-height:var(--table-min-height, 300px);overflow-x:auto;overflow-y:auto;background-color:var(--grid-surface)}.pivot-viewport .cdk-virtual-scroll-content-wrapper{width:auto;height:auto}.table-wrapper{min-width:100%;overflow-x:visible}.incremental-row-container .eru-grid-table,.eru-grid-table{width:100%!important;border-collapse:separate;border-spacing:0;table-layout:fixed!important;background-color:var(--grid-surface);color:var(--grid-on-surface);font-size:var(--grid-font-size-body);line-height:var(--grid-line-height-body)}.eru-grid-table th,.eru-grid-table td{text-align:left;overflow:hidden!important;text-overflow:ellipsis!important;white-space:nowrap!important;background-color:var(--grid-surface);color:var(--grid-on-surface);min-width:0;max-width:100%!important;box-sizing:border-box;position:relative}.eru-grid-table th:hover,.eru-grid-table td:hover{background-color:var(--grid-surface-variant)}.eru-grid-table thead{background-color:var(--grid-surface-container);transform:translateZ(0);will-change:transform;backface-visibility:hidden}.eru-grid-table thead.freeze-header-enabled{box-shadow:0 2px 4px #0000001a;border-bottom:2px solid var(--grid-outline)}.eru-grid-table thead th{background-color:var(--grid-surface-container);color:var(--grid-on-surface);font-weight:500;font-size:var(--grid-font-size-caption)}.checkbox-column{width:50px;min-width:50px;max-width:50px;text-align:center;border:1px solid var(--grid-outline);background-color:var(--grid-surface-container)}.checkbox-column input[type=checkbox]{width:16px;height:16px;cursor:pointer;accent-color:var(--grid-primary);border-radius:var(--grid-border-radius)}.checkbox-column input[type=checkbox]:focus{outline:2px solid var(--grid-primary);outline-offset:2px}.group-header{background-color:var(--grid-surface-container);color:var(--grid-on-surface);font-size:var(--grid-font-size-caption);font-weight:500;border-bottom:1px solid var(--grid-outline);cursor:pointer;transition:background-color .2s ease}.group-header:hover{background-color:var(--grid-surface-container-high)}.group-header .group-title{font-weight:600;color:var(--grid-primary)}.group-header .group-row-count{color:var(--grid-on-surface-variant);font-size:var(--grid-font-size-caption);margin-left:var(--grid-spacing-sm)}.row-item{border:1px solid var(--grid-outline);background-color:var(--grid-surface);transition:background-color .2s ease,box-shadow .2s ease}.row-item:hover{background-color:var(--grid-surface-variant);box-shadow:var(--grid-elevation-1)}.column-header{font-weight:500;text-align:center!important;font-size:var(--grid-font-size-caption, 12px);position:relative;-webkit-user-select:none;user-select:none;background-color:var(--grid-surface-container);color:var(--grid-on-surface)}.column-header:hover{background-color:var(--grid-surface-container-high)}.column-drag-handle{position:absolute;left:0;top:0;bottom:0;width:12px;cursor:grab;opacity:0;transition:opacity .2s ease,background-color .2s ease;z-index:2;display:flex;align-items:center;justify-content:center;border-right:1px solid transparent}.column-drag-handle:after{content:\"\\22ee\\22ee\";font-size:14px;color:var(--grid-on-surface-variant);transform:rotate(90deg)}.column-drag-handle:hover{background-color:var(--grid-surface-container-high);border-right-color:var(--grid-outline)}.column-header:hover .column-drag-handle{opacity:1}.column-drag-handle:active{cursor:grabbing}.dragging{opacity:1;background-color:var(--grid-surface-container);box-shadow:var(--grid-elevation-2)}.drag-over{background-color:var(--grid-surface-container);border-color:var(--grid-primary)}.data-cell{background-color:var(--grid-surface);color:var(--grid-on-surface);font-size:var(--grid-font-size-body)}.cell-content{align-items:center}.cell-content .mdc-text-field{padding:0px var(--grid-spacing-xxs)!important}.cell-display-text{align-items:center;padding:0px var(--grid-spacing-xs)}.ghost-loading-row{background-color:transparent}.ghost-cell-container{padding:var(--grid-spacing-sm)}.ghost-cell{height:20px;width:100%;background-color:var(--grid-surface-container);animation:pulse 1.5s ease-in-out infinite;border-radius:var(--grid-border-radius)}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.resizing{cursor:col-resize;-webkit-user-select:none;user-select:none}.column-resizer{position:absolute;right:0;top:0;bottom:0;width:4px;cursor:col-resize;background-color:transparent;transition:background-color .2s ease}.column-resizer:hover{background-color:var(--grid-primary)}.group-separator{height:var(--grid-spacing-sm);background-color:var(--grid-surface-variant)}.group-separator .separator-cell{background-color:var(--grid-surface-variant);border:none;height:var(--grid-spacing-sm)}.error-state{background-color:var(--grid-error-container);color:var(--grid-error);border-color:var(--grid-error)}.error-message{background-color:var(--grid-error);color:#fff;padding:var(--grid-spacing-sm);border-radius:var(--grid-border-radius);font-size:var(--grid-font-size-caption)}.incremental-row-container .eru-grid-table tbody,.incremental-row-container .eru-grid-table{position:relative}.incremental-row-container .eru-grid-table.show-column-lines{border-right:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important;border-top:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important;border-bottom:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important}.incremental-row-container .eru-grid-table:not(.show-column-lines){border:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important}.incremental-row-container .eru-grid-table thead:after{content:\"\";position:absolute;bottom:0;left:0;right:0;height:calc(var(--grid-outline-width, 1px) * 2);background-color:var(--grid-outline, #e0e0e0);pointer-events:none;z-index:10}.incremental-row-container .eru-grid-table.show-column-lines thead th{border-left:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important;border-bottom:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important}.incremental-row-container .eru-grid-table.show-column-lines tbody td{border-left:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important}.incremental-row-container .eru-grid-table.show-row-lines thead th{border-left:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important;border-bottom:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important}.incremental-row-container .eru-grid-table.show-row-lines tbody td{border-bottom:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important}@media (max-width: 768px){.incremental-row-container{height:600px}.eru-grid-table th,.eru-grid-table td{font-size:var(--grid-font-size-caption)}.checkbox-column{width:40px;min-width:40px;max-width:40px}}@media (prefers-contrast: high){.eru-grid-table th,.eru-grid-table td{border-width:2px}.row-item:hover{border-width:2px;border-color:var(--grid-primary)}}@media (prefers-reduced-motion: reduce){.row-item,.column-drag-handle,.ghost-cell{transition:none;animation:none}}.pivot-table .nested-header{text-align:center;font-weight:600;background:var(--grid-surface-container)}.pivot-table .nested-header.row-dimension-header{background:var(--grid-surface-container);font-weight:600}.pivot-table .pivot-header-leafcols{padding:0;margin:0;height:0}.pivot-table .pivot-header-level.level-0 .nested-header{font-size:14px;padding:12px 8px}.pivot-table .pivot-header-level.level-1 .nested-header{font-size:13px;padding:10px 6px}.pivot-table .pivot-header-level.level-2 .nested-header{font-size:12px;padding:8px 4px}.pivot-table .nested-header:hover{background:var(--grid-surface-variant);color:var(--grid-primary);transition:all .2s ease}.pivot-table .pivot-cell.aggregated-value{font-weight:500;font-family:Roboto Mono,monospace}.pivot-table .pivot-cell-content{display:flex;justify-content:center;align-items:center;min-height:38px}.pivot-table .rowspan-cell{vertical-align:middle}.pivot-table .rowspan-cell .pivot-cell-content{height:100%}.pivot-mode .incremental-row-container{display:flex;flex-direction:column;height:auto;max-height:85vh;overflow:auto}.pivot-mode .h-shell{position:relative;width:calc(100% - var(--scrollbar-width, 17px))!important;top:0;z-index:1;overflow-x:hidden;overflow-y:hidden;scrollbar-width:none;-ms-overflow-style:none}.pivot-mode .h-shell::-webkit-scrollbar{display:none}.pivot-mode .gt-shell{position:relative;bottom:50px;flex-shrink:0;overflow-x:hidden;overflow-y:hidden;scrollbar-width:none;-ms-overflow-style:none}.pivot-mode .gt-shell::-webkit-scrollbar{display:none}.pivot-mode .gt-shell table{border-bottom:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important}.pivot-mode .gt-shell.adjust-bottom-vs{bottom:66px!important}.pivot-mode .gt-shell.adjust-bottom:not(.adjust-bottom-vs){bottom:calc(66px - var(--scrollbar-width, 17px))!important}.pivot-mode .header-shell{flex-shrink:0;width:100%;box-sizing:border-box;padding-right:var(--scrollbar-width, 17px);overflow-x:auto;overflow-y:hidden;scrollbar-width:none;-ms-overflow-style:none}.pivot-mode .header-shell::-webkit-scrollbar{display:none}.pivot-mode .header-shell.apply-cdk-width{width:calc(var(--table-total-width) + 10px)!important}.pivot-mode .header-shell .eru-grid-table{margin-bottom:0;width:100%;table-layout:fixed}.pivot-mode .header-shell .eru-grid-table thead{background:var(--grid-surface-container)}.pivot-mode .header-shell .eru-grid-table thead th{background:var(--grid-surface-container);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:var(--col-width)!important;min-width:var(--col-width)!important;max-width:var(--col-width)!important}.pivot-mode .header-shell .eru-grid-table thead th.sticky-column{position:sticky;background:var(--grid-surface-container);z-index:111}.pivot-mode .header-shell .eru-grid-table tbody td{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:var(--col-width)!important;min-width:var(--col-width)!important;max-width:var(--col-width)!important}.pivot-mode .pivot-container{display:flex;flex-direction:column;height:100%;width:100%;overflow:hidden}.pivot-mode .pivot-table{width:auto!important;min-width:100%!important;table-layout:fixed!important;width:100%!important}.pivot-mode .pivot-table colgroup col{width:var(--col-width)!important;min-width:var(--col-width)!important;max-width:var(--col-width)!important;flex:none!important;flex-shrink:0!important;flex-grow:0!important}.pivot-mode .pivot-table td,.pivot-mode .pivot-table th{box-sizing:border-box!important;flex:none!important;flex-shrink:0!important;flex-grow:0!important;word-wrap:break-word!important;word-break:break-all!important}.pivot-mode .pivot-table *{max-width:var(--col-width)!important;box-sizing:border-box!important}.pivot-mode .pivot-table colgroup{width:100%!important}.pivot-mode .pivot-table colgroup col{width:var(--col-width)!important;min-width:var(--col-width)!important;max-width:var(--col-width)!important;flex-basis:var(--col-width)!important;flex:0 0 var(--col-width)!important}.pivot-mode .pivot-table table{width:100%!important;table-layout:fixed!important;border-collapse:collapse!important;border-spacing:0!important}.pivot-mode .pivot-table[style*=--table-total-width]{width:var(--table-total-width)!important;min-width:var(--table-total-width)!important;max-width:var(--table-total-width)!important}.pivot-mode .pivot-table colgroup col{width:var(--col-width)!important;min-width:var(--col-width)!important;max-width:var(--col-width)!important;flex:0 0 var(--col-width)!important;flex-basis:var(--col-width)!important;flex-grow:0!important;flex-shrink:0!important;overflow:hidden!important}.pivot-mode .pivot-table tbody td,.pivot-mode .pivot-table thead th{width:var(--col-width)!important;min-width:var(--col-width)!important;max-width:var(--col-width)!important;overflow:hidden!important;text-overflow:ellipsis!important;white-space:nowrap!important}.pivot-mode .pivot-table .cell-content,.pivot-mode .pivot-table data-cell{width:100%!important;max-width:100%!important;overflow:hidden!important;text-overflow:ellipsis!important;white-space:nowrap!important;display:block!important}.pivot-mode .pivot-table table{width:var(--table-total-width)!important;min-width:var(--table-total-width)!important;max-width:var(--table-total-width)!important;table-layout:fixed!important;border-collapse:collapse!important;border-spacing:0!important;word-wrap:break-word!important;word-break:break-all!important}.pivot-mode .pivot-tbody tr.pivot-row{min-height:50px!important;height:50px!important}.pivot-mode .pivot-tbody tr.pivot-row:hover{background-color:var(--grid-surface-variant)}.pivot-mode .pivot-tbody tr.pivot-row:nth-child(2n){background-color:#00000005}.pivot-mode .pivot-tbody tr.pivot-row td{min-height:50px!important;height:50px!important;vertical-align:middle;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:var(--col-width)!important;min-width:var(--col-width)!important;max-width:var(--col-width)!important}.pivot-mode .pivot-tbody tr.pivot-row td .cell-content{min-height:48px;display:flex;align-items:center;justify-content:center}.pivot-mode .pivot-tbody tr.pivot-row td .cell-content data-cell{width:100%;min-height:46px;display:flex;align-items:center;justify-content:center;overflow:hidden;flex-shrink:0}.pivot-mode .pivot-cell{vertical-align:middle;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:var(--col-width)!important;min-width:var(--col-width)!important;max-width:var(--col-width)!important}.pivot-mode .pivot-cell.aggregated-value{font-weight:500;font-family:Roboto Mono,monospace}.pivot-mode .pivot-cell .cell-content{display:flex;justify-content:center;align-items:center;min-height:40px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex-shrink:0}.pivot-mode .pivot-table .subtotal-row{background-color:var(--grid-surface-container)!important;font-weight:600}.pivot-mode .pivot-table .subtotal-row td{background-color:var(--grid-surface-container);color:var(--grid-on-surface-variant)}.pivot-mode .pivot-table .subtotal-row td:first-child{color:var(--grid-primary)}.pivot-mode .pivot-table .subtotal-row td.aggregated-value{font-weight:500;color:var(--grid-primary)}.pivot-mode .pivot-table .subtotal-row:hover{background-color:var(--grid-surface-container-high)!important}.pivot-mode .pivot-table .subtotal-row:hover td{background-color:var(--grid-surface-container-high)}.pivot-mode .pivot-table .subtotal-bold td{font-weight:600!important;font-style:normal!important}.pivot-mode .pivot-table .subtotal-bold td.aggregated-value{font-weight:600!important}.pivot-mode .pivot-table .subtotal-italic td{font-style:italic!important}.pivot-mode .pivot-table .subtotal-italic td:first-child{font-weight:600!important}.pivot-mode .pivot-table .subtotal-italic td.aggregated-value{font-style:italic!important;font-weight:500!important}.pivot-mode .pivot-table .subtotal-highlighted{background-color:var(--grid-surface-variant)!important}.pivot-mode .pivot-table .subtotal-highlighted td{background-color:var(--grid-surface-variant)!important;font-weight:700!important;font-style:normal!important;color:var(--grid-primary)!important}.pivot-mode .pivot-table .subtotal-highlighted td.aggregated-value{font-weight:500!important;color:var(--grid-primary)!important}.pivot-mode .pivot-table .subtotal-highlighted:hover,.pivot-mode .pivot-table .subtotal-highlighted:hover td{background-color:var(--grid-surface-container-high)!important}.pivot-mode .pivot-table .grand-total-row{background-color:var(--grid-surface-container-high)!important;font-weight:700;font-size:var(--grid-font-size-body)}.pivot-mode .pivot-table .grand-total-row td{background-color:var(--grid-surface-container-high)!important;color:var(--grid-on-surface)}.pivot-mode .pivot-table .grand-total-row td:first-child{font-style:normal;font-weight:800;color:var(--grid-primary)}.pivot-mode .pivot-table .grand-total-row td.aggregated-value{font-weight:500;color:var(--grid-primary);font-family:Roboto Mono,monospace}.pivot-mode .pivot-table .grand-total-row:hover,.pivot-mode .pivot-table .grand-total-row:hover td{background-color:var(--grid-surface-container-high)!important}.pivot-mode .pivot-table .grand-total-bold td{font-weight:700!important;font-style:normal!important}.pivot-mode .pivot-table .grand-total-bold td.aggregated-value{font-weight:700!important}.pivot-mode .pivot-table .grand-total-italic td,.pivot-mode .pivot-table .grand-total-italic td.aggregated-value{font-style:italic!important;font-weight:500!important}.pivot-mode .pivot-table .grand-total-highlighted{background-color:var(--grid-primary)!important;box-shadow:var(--grid-elevation-2)!important}.pivot-mode .pivot-table .grand-total-highlighted td{background-color:var(--grid-primary)!important;color:var(--grid-on-primary)!important;font-weight:500!important;font-style:normal!important}.pivot-mode .pivot-table .grand-total-highlighted td.aggregated-value{color:var(--grid-on-primary)!important;font-weight:500!important}.pivot-mode .pivot-table .grand-total-highlighted:hover,.pivot-mode .pivot-table .grand-total-highlighted:hover td{background-color:var(--grid-primary)!important}.pivot-mode .pivot-table .collapsible-header{position:relative}.pivot-mode .pivot-table .collapsible-header .header-content{display:flex;align-items:center;justify-content:space-between;gap:var(--grid-spacing-xs);padding:var(--grid-spacing-xs) var(--grid-spacing-sm)}.pivot-mode .pivot-table .collapsible-header .header-label{flex:1;font-weight:600}.pivot-mode .pivot-table .collapsible-header .collapse-toggle-btn{background:none;border:none;cursor:pointer;padding:var(--grid-spacing-xxs);margin:0;display:flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:var(--grid-border-radius);color:var(--grid-on-surface-variant);transition:all .2s ease;font-size:12px;font-weight:600}.pivot-mode .pivot-table .collapsible-header .collapse-toggle-btn:hover{background-color:var(--grid-surface-container);color:var(--grid-primary);transform:scale(1.1)}.pivot-mode .pivot-table .collapsible-header .collapse-toggle-btn:focus{outline:2px solid var(--grid-primary);outline-offset:1px}.pivot-mode .pivot-table .collapsible-header .collapse-toggle-btn .collapse-icon{display:block;line-height:1;font-family:monospace;font-size:14px}.pivot-mode .pivot-table .collapsible-header.expanded .collapse-toggle-btn .collapse-icon{color:var(--grid-primary)}.pivot-mode .pivot-table .collapsible-header.collapsed{background-color:var(--grid-surface-variant)}.pivot-mode .pivot-table .collapsible-header.collapsed .header-label{font-style:italic;color:var(--grid-on-surface-variant)}.pivot-mode .pivot-table .collapsible-header.collapsed .collapse-toggle-btn .collapse-icon{color:var(--grid-outline)}.pivot-mode .pivot-table .collapsible-header:hover{background-color:var(--grid-surface-container)}.pivot-mode .pivot-table .collapsible-header:hover .header-label{color:var(--grid-on-surface)}.pivot-mode .pivot-table .pivot-single-table{display:flex;flex-direction:column;height:100%;width:100%;overflow:hidden;min-height:var(--table-min-height)!important}.pivot-mode .pivot-table .pivot-single-table .pivot-header-container{flex-shrink:0;background:var(--grid-surface)!important;overflow-x:auto;overflow-y:hidden;min-height:100px!important;height:auto!important}.pivot-mode .pivot-table .pivot-single-table .pivot-header-container .pivot-table{width:auto;min-width:100%;height:auto!important;min-height:100px!important}.pivot-mode .pivot-table .pivot-single-table .pivot-header-container .pivot-table th{background:var(--grid-surface-container)!important;padding:8px 6px!important;white-space:nowrap;min-width:50px;min-height:40px!important;height:auto!important;position:relative;visibility:visible!important;color:var(--grid-on-surface)!important}.pivot-mode .pivot-table .pivot-single-table .pivot-header-container .pivot-table th.sticky-column{position:sticky!important;background:var(--grid-surface-container)!important;border-right:2px solid var(--grid-primary)!important;box-shadow:2px 0 4px #0000001a;z-index:101!important}.pivot-mode .pivot-table .pivot-single-table .pivot-data-container{flex:1;overflow:auto;min-height:300px!important;height:auto!important}.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-viewport{height:100%!important;width:100%!important;overflow-x:auto!important;overflow-y:auto!important}.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-table,.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-data-table{width:auto;min-width:100%;height:auto!important}.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-table td,.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-data-table td{padding:8px 6px!important;white-space:nowrap;min-width:50px;min-height:32px!important;height:auto!important;background:var(--grid-surface)!important;color:var(--grid-on-surface)!important;visibility:visible!important}.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-table td.sticky-column,.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-data-table td.sticky-column{position:sticky!important;background:var(--grid-surface-container)!important;border-right:2px solid var(--grid-primary)!important;box-shadow:2px 0 4px #0000001a;z-index:100!important}.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-table tbody tr,.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-data-table tbody tr{height:auto!important;min-height:50px!important}.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-table tbody tr.pivot-row,.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-data-table tbody tr.pivot-row{visibility:visible!important;display:table-row!important}.pivot-mode .pivot-table .collapsed-column-group{background-color:var(--grid-surface-container);border-left:3px solid var(--grid-primary)}.pivot-mode .pivot-table .collapsed-column-group:hover{background-color:var(--grid-surface-container-high)}.pivot-row.subtotal-row{background-color:var(--grid-surface-variant);font-weight:500}.pivot-row.subtotal-row.subtotal-bold{font-weight:500}.pivot-row.subtotal-row.subtotal-italic{font-style:italic}.pivot-row.subtotal-row.subtotal-highlighted{background-color:var(--grid-primary);color:var(--grid-on-primary)}.pivot-row.grand-total-row{background-color:var(--grid-surface-container);font-weight:600}.pivot-row.grand-total-row.grand-total-bold{font-weight:800}.pivot-row.grand-total-row.grand-total-italic{font-style:italic}.pivot-row.grand-total-row.grand-total-highlighted{background-color:var(--grid-primary);color:var(--grid-on-primary)}.pivot-row.first-visible-row{background-color:#6750a41a!important;position:relative}.pivot-row.first-visible-row:before{content:\"\\1f441\\fe0f First Visible\";position:absolute;top:-20px;left:0;background:var(--grid-primary);color:var(--grid-on-primary);padding:2px 6px;font-size:10px;border-radius:2px;z-index:1000}.header-wrap-text{white-space:pre-wrap;word-break:auto-phrase}\n"] }]
5775
+ ], template: "<!-- <div style=\"background: #f0f0f0; font-size: 12px; border-bottom: 1px solid #ccc;\">\n currentPivotScrollIndex {{currentPivotScrollIndex()}} | \n firstDataRowIndex {{firstDataRowIndex()}} | \n firstTr {{firstTr}} |\n maxDepth {{maxDepth()}}\n</div> -->\n<div class=\"incremental-row-container eru-grid\" #rowContainer \n [class.pivot-mode]=\"gridStore.isPivotMode()\"\n [class.table-mode]=\"!gridStore.isPivotMode()\">\n \n <!-- Pivot Mode Template -->\n @if (gridStore.isPivotMode()) {\n <ng-container >\n <div class=\"pivot-container\" style=\"display: flex; flex-direction: column; height: 100%;\"\n [style]=\"'--table-min-height: ' + getInitialMinHeightPx() + 'px; --table-total-width: ' + getInitialTotalWidth() + 'px'\">\n <!-- Debug info for first visible row -->\n \n \n <div class=\"pivot-single-table\" style=\"height: 100%; width: 100%; overflow: hidden; display: flex; flex-direction: column;\">\n @if (freezeHeader()) {\n <div #headerScroller class=\"header-shell\">\n <table class=\"eru-grid-table pivot-table\" \n [style]=\"'width: auto; min-width: 100%; --table-total-width: ' + getInitialTotalWidth() + 'px'\"\n [class.show-column-lines]=\"showColumnLines()\" \n [class.show-row-lines]=\"showRowLines()\">\n <!-- Column Groups for consistent width -->\n <ng-container *ngTemplateOutlet=\"pivotColGroup\"></ng-container>\n \n <ng-container *ngTemplateOutlet=\"pivotTableHead\"></ng-container>\n @if(grandTotalPosition() === 'before' && freezeGrandTotal()) {\n <ng-container *ngTemplateOutlet=\"pivotGrandTotal\"></ng-container>\n }\n </table> \n </div>\n } \n <!-- Virtual Scrolled Table Body -->\n <div>\n <cdk-virtual-scroll-viewport\n #vp\n [itemSize]=\"50\"\n class=\"viewport pivot-viewport\"\n [class.apply-cdk-width]=\"applyCdkWidth()\"\n (scrolledIndexChange)=\"onPivotScroll($event)\"\n (scroll)=\"onBodyScroll($event)\"\n style=\"overflow: auto;\">\n <table class=\"eru-grid-table pivot-table\" \n [style]=\"'width: auto; min-width: 100%; --table-total-width: ' + getInitialTotalWidth() + 'px'\"\n [class.show-column-lines]=\"showColumnLines()\" \n [class.show-row-lines]=\"showRowLines()\"\n >\n <!-- Column Groups for consistent width -->\n <ng-container *ngTemplateOutlet=\"pivotColGroup\"></ng-container>\n \n @if (!freezeHeader()) {\n <ng-container *ngTemplateOutlet=\"pivotTableHead\"></ng-container> \n }\n <!-- Table Body with Virtual Scrolling -->\n <tbody class=\"pivot-tbody\">\n \n <tr *cdkVirtualFor=\"let pivotRow of gridStore.pivotDisplayData(); \n trackBy: trackByPivotRowFn; \n let i = index\"\n class=\"pivot-row\"\n [class.subtotal-row]=\"pivotRow._isSubtotal\"\n [class.grand-total-row]=\"pivotRow._isGrandTotal\"\n [class.subtotal-bold]=\"pivotRow._isSubtotal && subTotalStyle() === 'bold'\"\n [class.subtotal-italic]=\"pivotRow._isSubtotal && subTotalStyle() === 'italic'\"\n [class.subtotal-highlighted]=\"pivotRow._isSubtotal && subTotalStyle() === 'highlighted'\"\n [class.grand-total-bold]=\"pivotRow._isGrandTotal && grandTotalStyle() === 'bold'\"\n [class.grand-total-italic]=\"pivotRow._isGrandTotal && grandTotalStyle() === 'italic'\"\n [class.grand-total-highlighted]=\"pivotRow._isGrandTotal && grandTotalStyle() === 'highlighted'\"\n [style.height.px]=\"50\"\n [attr.data-pivot-row]=\"i\">\n @if ((!pivotRow._isGrandTotal && freezeGrandTotal() ) || (!freezeGrandTotal() )) { \n @for (column of getLeafColumns(); track trackByColumnFn($index, column); let colIndex = $index) {\n @if (!shouldSkipCell(i, column.name)) {\n <td\n [attr.rowspan]=\"getEffectiveRowspan(i, column.name)\"\n [style.width.px]=\"column.field_size\"\n [style.minWidth.px]=\"column.field_size\"\n class=\"pivot-cell\"\n [class.row-dimension-cell]=\"isRowDimensionColumn(column.name)\"\n [class.column-dimension-cell]=\"!isRowDimensionColumn(column.name)\"\n [class.aggregated-value]=\"!isRowDimensionColumn(column.name) && column.datatype === 'number'\"\n [class.rowspan-cell]=\"getEffectiveRowspan(i, column.name) || 1 > 1\"\n [class.sticky-column]=\"isStickyColumn(column.name, colIndex)\"\n [style.position]=\"isStickyColumn(column.name, colIndex) ? 'sticky' : 'static'\"\n [style.left.px]=\"getStickyColumnLeft(column.name, colIndex)\"\n [style.z-index]=\"isStickyColumn(column.name, colIndex) ? 99 : 1\"\n [style.height.px]=\"50\"\n [attr.xx]=\"i\"> \n <div class=\"cell-content pivot-cell-content\">\n <data-cell\n [class.aggregation]=\"!!column.aggregationFunction\"\n [fieldSize]=\"column.field_size\"\n [columnDatatype]=\"column.datatype\"\n [columnName]=\"column.name\"\n [value]=\"getEffectiveCellValue(i,column.name, pivotRow)\"\n [column]=\"column\"\n [drillable]=\"column.enableDrilldown || false\"\n [mode]=\"mode()\"\n [isEditable]=\"isEditable()\"\n [id]=\"'pivot_' + i + '_' + column.name\"\n [eruGridStore]=\"gridStore\">\n </data-cell>\n </div>\n </td>\n }\n }\n } @else {\n <td [style.height.px]=\"50\" [attr.colspan]=\"getLeafColumns().length\"> </td>\n }\n </tr> \n </tbody>\n </table>\n </cdk-virtual-scroll-viewport>\n\n </div>\n @if (freezeGrandTotal() && grandTotalPosition() === 'after') {\n <div #gtScroller class=\"header-shell gt-shell\"\n [class.adjust-bottom]=\"!applyCdkWidth()\"\n [class.adjust-bottom-vs]=\"adjustScrollWidth()\">\n <table class=\"eru-grid-table pivot-table\" \n [style]=\"'width: auto; min-width: 100%; --table-total-width: ' + getInitialTotalWidth() + 'px'\"\n [class.show-column-lines]=\"showColumnLines()\" \n [class.show-row-lines]=\"showRowLines()\">\n <ng-container *ngTemplateOutlet=\"pivotColGroup\"></ng-container>\n \n <ng-container *ngTemplateOutlet=\"pivotGrandTotal\"></ng-container>\n \n </table> \n </div>\n }\n \n\n </div>\n </div>\n </ng-container>\n} @else {\n \n<!-- Table Mode Template -->\n <ng-container>\n <cdk-virtual-scroll-viewport\n [itemSize]=\"30\"\n class=\"viewport table-viewport\"\n (scrolledIndexChange)=\"onScroll($event)\"\n [style]=\"'--table-min-height: ' + getInitialMinHeightPx() + 'px; --table-total-width: ' + getInitialTotalWidth() + 'px'\"\n >\n <div class=\"table-wrapper\">\n \n <table class=\"eru-grid-table\" [class.show-column-lines]=\"showColumnLines()\" [class.show-column-lines]=\"showColumnLines()\">\n <thead>\n <tr>\n @if(gridStore.configuration().config.allowSelection) {\n <th class=\"checkbox-column\"></th>\n }\n <th *ngFor=\"let column of columns(); trackBy: trackByColumnFn\"\n [style.width.px]=\"column.field_size\"\n [resizeColumn]=\"gridStore.isFeatureEnabled('columnResizable')\"\n [style.minWidth.px]=\"column.field_size\"\n class=\"column-header table-column-header\">\n {{column.label}}\n </th>\n </tr>\n </thead>\n <tbody *ngIf=\"columns() as columnsList\">\n <ng-container *cdkVirtualFor=\"let groupItem of groupedRows();\n trackBy: trackByGroupItemFn;\n let i = index;\n let first=first\">\n <tr\n *ngIf=\"groupItem.type === 'header' && groups().length > 1\"\n class=\"group-header\"\n (click)=\"toggleGroupExpansion(groupItem.group?.id || '')\"\n >\n \n <td class=\"checkbox-column\" style=\"border: none;\">\n {{ groupItem.group?.isExpanded ? '\u25BC' : '\u25B6' }}\n </td>\n \n <td [attr.colspan]=\"2\" style=\"border: none;\">\n <span class=\"group-title\">\n {{ groupItem.group?.title }}\n </span>\n <span class=\"group-row-count\">\n ({{ groupItem.group?.currentLoadedRows || 0 }}/{{ groupItem.group?.totalRowCount || 0 }})\n </span>\n </td>\n </tr>\n <tr *ngIf=\"groupItem.type === 'table-header' && groups().length > 1\" style=\"background:#fafafa\">\n @if(gridStore.configuration().config.allowSelection) {\n <th class=\"checkbox-column\" style=\"text-align: center;\">\n <input\n type=\"checkbox\"\n [checked]=\"isGroupSelected(groupItem.group?.id || '')\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"toggleGroupSelection($event, groupItem.group?.id || '')\"\n >\n </th>\n }\n <th *ngFor=\"let column of columns(); trackBy: trackByColumnFn;let i =index\"\n style=\"text-align: center;\"\n [style.width.px]=\"column.field_size\"\n [style.minWidth.px]=\"column.field_size\"\n [resizeColumn]=\"true\"\n [columnConfig]=\"column\"\n [columnDraggable]=\"i\"\n class=\"column-header\">\n <div class=\"column-drag-handle\"></div>\n {{column.label}} {{column.symbol}}\n </th>\n </tr>\n <tr\n *ngIf=\"groupItem.type === 'row' && groupItem.group?.isExpanded\"\n class=\"row-item\"\n [attr.data-row-id]=\"groupItem.row?.entity_id\"\n [style.height.px]=\"30\"\n [style.minHeight.px]=\"30\"\n [style.maxHeight.px]=\"30\"\n >\n @if(gridStore.configuration().config.allowSelection) {\n <td class=\"checkbox-column\" style=\"text-align: center;\">\n <input\n type=\"checkbox\"\n [checked]=\"isRowSelected(groupItem.row?.entity_id)\"\n (change)=\"toggleRowSelection($event, groupItem.row)\"\n >\n </td>\n }\n <td #cell *ngFor=\"let column of columns(); trackBy: trackByColumnFn\"\n [style.width.px]=\"column.field_size\"\n [style.minWidth.px]=\"column.field_size\"\n class=\"data-cell\"\n [style.height.px]=\"30\"\n [style.minHeight.px]=\"30\"\n [style.maxHeight.px]=\"30\"\n [matTooltipClass]=\"'error-message'\"\n [matTooltip]=\"datacell.error()?'Error: ' + datacell.error():''\"\n matTooltipPosition=\"below\"\n >\n <div class=\"cell-content\">\n <data-cell\n #datacell \n [td]=cell\n [fieldSize]=\"column.field_size\"\n [columnDatatype]=\"column.datatype\"\n [columnName]=\"column.name\"\n [value]=\"groupItem.row?.[column.name] || ''\"\n [column]=\"column\"\n [mode]=\"mode()\"\n [isEditable]=\"isEditable()\"\n [drillable]=\"column.enableDrilldown || false\"\n [id]=\"i + '_' + column.name\"\n [eruGridStore]=\"gridStore\"\n ></data-cell>\n </div>\n </td>\n </tr>\n\n <tr\n *ngIf=\"groupItem.type === 'ghost-loading' && groupItem.group?.isExpanded\"\n class=\"ghost-loading-row\"\n [style.height.px]=\"30\"\n [style.minHeight.px]=\"30\"\n [style.maxHeight.px]=\"30\"\n >\n @if(gridStore.configuration().config.allowSelection) {\n <th class=\"checkbox-column\"></th>\n }\n <td *ngFor=\"let column of columns(); trackBy: trackByColumnFn\"\n [style.width.px]=\"column.field_size\"\n [style.minWidth.px]=\"column.field_size\"\n class=\"ghost-cell-container\"\n >\n <div class=\"ghost-cell\"></div>\n </td>\n </tr>\n\n <tr\n *ngIf=\"groupItem.type === 'row-place-holder'\"\n class=\"group-separator\"\n >\n <td [attr.colspan]=\"groupSeperatorColSpan()\" class=\"separator-cell\"></td>\n </tr>\n </ng-container>\n </tbody>\n </table>\n </div>\n </cdk-virtual-scroll-viewport>\n </ng-container> \n}\n</div>\n\n <!-- Pivot Table Header Template -->\n <ng-template #pivotTableHead>\n <thead>\n @if (hasNestedHeaders()) {\n <ng-container >\n @for (headerRow of getHeaderRows(); track headerRow; let rowIndex = $index) {\n <tr class=\"pivot-header pivot-header-container\" \n [class.pivot-header-level]=\"'level-' + rowIndex\">\n @for (header of headerRow; track trackByHeaderFn($index, header); let colIndex = $index) {\n <th\n [attr.colspan]=\"header.colspan\"\n [attr.rowspan]=\"header.rowspan\"\n [resizeColumn]=\"gridStore.isFeatureEnabled('columnResizable') && header.level === 0\"\n class=\"column-header pivot-column-header nested-header\"\n [class.row-dimension-header]=\"isRowDimensionHeader(header)\"\n [class.column-dimension-header]=\"!isRowDimensionHeader(header)\"\n [class.expanded]=\"header.isExpanded\"\n [class.collapsed]=\"!header.isExpanded\"\n [class.sticky-column]=\"isStickyColumn(header.name, colIndex)\"\n [style.position]=\"isStickyColumn(header.name, colIndex) ? 'sticky' : 'static'\"\n [style.left.px]=\"getStickyColumnLeft(header.name, colIndex)\"\n [style.z-index]=\"isStickyColumn(header.name, colIndex) ? 100 : 1\"\n style=\"min-height: 40px; height: auto; padding: 8px 6px;\">\n <div class=\"header-content\">\n \n <data-cell\n [fieldSize]=\"header.field_size\"\n [columnDatatype]=\"header.dataType\"\n [columnName]=\"header.name\"\n [value]=\"header.label\"\n [column]=\"header\"\n [frozenGrandTotalCell]=\"true\"\n [drillable]=\"header.enableDrilldown || false\"\n [mode]=\"mode()\"\n [isEditable]=\"isEditable()\"\n [id]=\"'pivot_' + $index + '_' + header.name\"\n [eruGridStore]=\"gridStore\">\n </data-cell>\n <!-- <span class=\"header-label header-wrap-text\">{{header.label}}</span> -->\n <!-- <button *ngIf=\"!isRowDimensionHeader(header)\" \n class=\"collapse-toggle-btn\"\n [title]=\"header.isExpanded ? 'Collapse group' : 'Expand group'\"\n (click)=\"toggleColumnGroup(header.groupKey)\"\n type=\"button\">\n <span class=\"collapse-icon\">+</span>\n </button> -->\n </div>\n </th>\n }\n </tr>\n }\n </ng-container>\n } @else {\n <!-- Simple header fallback -->\n <ng-container>\n <tr class=\"pivot-header\" [class.freeze-header-enabled]=\"freezeHeader()\">\n @for (column of getLeafColumns(); track trackByColumnFn($index, column); let colIndex = $index) {\n <th [style.width.px]=\"column.field_size\"\n [style.minWidth.px]=\"column.field_size\"\n [resizeColumn]=\"gridStore.isFeatureEnabled('columnResizable')\"\n [columnConfig]=\"column\"\n class=\"column-header pivot-column-header\"\n [class.sticky-column]=\"isStickyColumn(column.name, colIndex)\"\n [style.position]=\"isStickyColumn(column.name, colIndex) ? 'sticky' : 'static'\"\n [style.left.px]=\"getStickyColumnLeft(column.name, colIndex)\"\n [style.z-index]=\"isStickyColumn(column.name, colIndex) ? 100 : 1\"\n style = \"min-height: 40px;height: auto;padding: 8px 6px;position: relative;left: 0px;z-index: 1\">\n {{column.label}}\n </th>\n }\n </tr>\n </ng-container>\n }\n \n </thead>\n</ng-template>\n\n<!-- Column Group Template for consistent column widths -->\n<ng-template #pivotColGroup>\n <colgroup>\n @for (column of getLeafColumns(); track trackByColumnFn($index, column)) {\n <col [style]=\"'width: ' + column.field_size + 'px !important; min-width: ' + column.field_size + 'px !important; max-width: ' + column.field_size + 'px !important; --col-width: ' + column.field_size + 'px'\">\n }\n </colgroup>\n</ng-template>\n\n<ng-template #pivotGrandTotal>\n<tbody class=\"pivot-tbody\">\n @for (pivotRow of gridStore.pivotGrandTotalData(); track trackByPivotRowFn($index, pivotRow); let i = $index) {\n <tr \n class=\"pivot-row grand-total-row\"\n [class.grand-total-bold]=\"pivotRow._isGrandTotal && grandTotalStyle() === 'bold'\"\n [class.grand-total-italic]=\"pivotRow._isGrandTotal && grandTotalStyle() === 'italic'\"\n [class.grand-total-highlighted]=\"pivotRow._isGrandTotal && grandTotalStyle() === 'highlighted'\"\n [style.height.px]=\"50\"\n [attr.data-pivot-row]=\"i\">\n <!-- <td colspan=\"20\">{{pivotRow | json}}</td> -->\n @for (column of getLeafColumns(); track trackByColumnFn($index, column); let colIndex = $index) {\n <td\n [attr.rowspan]=\"getEffectiveRowspan(i, column.name)\" \n [style.width.px]=\"column.field_size\"\n [style.minWidth.px]=\"column.field_size\"\n class=\"pivot-cell\"\n [class.row-dimension-cell]=\"isRowDimensionColumn(column.name)\"\n [class.column-dimension-cell]=\"!isRowDimensionColumn(column.name)\"\n [class.aggregated-value]=\"!isRowDimensionColumn(column.name) && column.datatype === 'number'\"\n [class.rowspan-cell]=\"getEffectiveRowspan(i, column.name) || 1 > 1\"\n [class.sticky-column]=\"isStickyColumn(column.name, colIndex)\"\n [style.position]=\"isStickyColumn(column.name, colIndex) ? 'sticky' : 'static'\"\n [style.left.px]=\"getStickyColumnLeft(column.name, colIndex)\"\n [style.z-index]=\"isStickyColumn(column.name, colIndex) ? 99 : 1\"\n [style.height.px]=\"50\"\n [attr.xx]=\"i\"> \n <div class=\"cell-content pivot-cell-content\">\n <data-cell\n [fieldSize]=\"column.field_size\"\n [columnDatatype]=\"column.datatype\"\n [columnName]=\"column.name\"\n [value]=\"getEffectiveCellValue(i,column.name, pivotRow)\"\n [column]=\"column\"\n [frozenGrandTotalCell]=\"true\"\n [drillable]=\"column.enableDrilldown || false\"\n [mode]=\"mode()\"\n [isEditable]=\"isEditable()\"\n [id]=\"'pivot_' + i + '_' + column.name\"\n [eruGridStore]=\"gridStore\">\n </data-cell>\n </div>\n </td>\n }\n </tr>\n}\n</tbody>\n</ng-template>\n", styles: ["@charset \"UTF-8\";:host{display:block;width:100%;height:100%;font-family:var(--grid-font-family);--grid-primary: #6750a4;--grid-on-primary: #ffffff;--grid-surface: #fef7ff;--grid-surface-variant: #e7e0ec;--grid-surface-container: #f3edf7;--grid-surface-container-high: #ede7f0;--grid-on-surface: #1d1b20;--grid-on-surface-variant: #49454f;--grid-outline: #79757f;--grid-outline-variant: #cac4d0;--grid-error: #ba1a1a;--grid-error-container: #ffdad6;--grid-font-family: \"Poppins\", \"Roboto\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;--grid-font-size-body: 12px;--grid-font-size-caption: 12px !important;--grid-line-height-body: 1;--grid-aggregation-text-align: right;--grid-number-text-align: right;--grid-spacing-xxs: 2px;--grid-spacing-xs: 4px;--grid-spacing-sm: 8px;--grid-spacing-md: 16px;--grid-spacing-lg: 24px;--grid-border-radius: 4px;--grid-elevation-1: 0px 1px 2px 0px rgba(0, 0, 0, .3), 0px 1px 3px 1px rgba(0, 0, 0, .15);--grid-elevation-2: 0px 1px 2px 0px rgba(0, 0, 0, .3), 0px 2px 6px 2px rgba(0, 0, 0, .15)}.incremental-row-container{padding:10px;width:100%;height:100%;min-height:var(--table-min-height, 400px);max-height:none;overflow:auto;position:relative;background-color:var(--grid-surface);border-radius:var(--grid-border-radius);box-shadow:var(--grid-elevation-1);font-family:var(--grid-font-family)}.viewport{overflow-x:auto;overflow-y:auto;background-color:var(--grid-surface);scrollbar-gutter:stable}.viewport.apply-cdk-width{width:calc(var(--table-total-width) + 10px)!important}.table-viewport{background-color:var(--grid-surface);height:auto;min-height:var(--table-min-height, 300px);max-height:80vh;overflow-x:auto;overflow-y:auto}.pivot-viewport{min-height:var(--table-min-height, 300px);overflow-x:auto;overflow-y:auto;background-color:var(--grid-surface)}.pivot-viewport .cdk-virtual-scroll-content-wrapper{width:auto;height:auto}.table-wrapper{min-width:100%;overflow-x:visible}.incremental-row-container .eru-grid-table,.eru-grid-table{width:100%!important;border-collapse:separate;border-spacing:0;table-layout:fixed!important;background-color:var(--grid-surface);color:var(--grid-on-surface);font-size:var(--grid-font-size-body);line-height:var(--grid-line-height-body)}.eru-grid-table th,.eru-grid-table td{text-align:left;overflow:hidden!important;text-overflow:ellipsis!important;white-space:nowrap!important;background-color:var(--grid-surface);color:var(--grid-on-surface);min-width:0;max-width:100%!important;box-sizing:border-box;position:relative}.eru-grid-table th:hover,.eru-grid-table td:hover{background-color:var(--grid-surface-variant)}.eru-grid-table thead{background-color:var(--grid-surface-container);transform:translateZ(0);will-change:transform;backface-visibility:hidden}.eru-grid-table thead.freeze-header-enabled{box-shadow:0 2px 4px #0000001a;border-bottom:2px solid var(--grid-outline)}.eru-grid-table thead th{background-color:var(--grid-surface-container);color:var(--grid-on-surface);font-weight:500;font-size:var(--grid-font-size-caption)}.checkbox-column{width:50px;min-width:50px;max-width:50px;text-align:center;border:1px solid var(--grid-outline);background-color:var(--grid-surface-container)}.checkbox-column input[type=checkbox]{width:16px;height:16px;cursor:pointer;accent-color:var(--grid-primary);border-radius:var(--grid-border-radius)}.checkbox-column input[type=checkbox]:focus{outline:2px solid var(--grid-primary);outline-offset:2px}.group-header{background-color:var(--grid-surface-container);color:var(--grid-on-surface);font-size:var(--grid-font-size-caption);font-weight:500;border-bottom:1px solid var(--grid-outline);cursor:pointer;transition:background-color .2s ease}.group-header:hover{background-color:var(--grid-surface-container-high)}.group-header .group-title{font-weight:600;color:var(--grid-primary)}.group-header .group-row-count{color:var(--grid-on-surface-variant);font-size:var(--grid-font-size-caption);margin-left:var(--grid-spacing-sm)}.row-item{border:1px solid var(--grid-outline);background-color:var(--grid-surface);transition:background-color .2s ease,box-shadow .2s ease}.row-item:hover{background-color:var(--grid-surface-variant);box-shadow:var(--grid-elevation-1)}.table-column-header{padding:12px 8px}.column-header{font-weight:500;text-align:center!important;font-size:var(--grid-font-size-caption, 12px);position:relative;-webkit-user-select:none;user-select:none;background-color:var(--grid-surface-container);color:var(--grid-on-surface)}.column-header:hover{background-color:var(--grid-surface-container-high)}.column-drag-handle{position:absolute;left:0;top:0;bottom:0;width:12px;cursor:grab;opacity:0;transition:opacity .2s ease,background-color .2s ease;z-index:2;display:flex;align-items:center;justify-content:center;border-right:1px solid transparent}.column-drag-handle:after{content:\"\\22ee\\22ee\";font-size:14px;color:var(--grid-on-surface-variant);transform:rotate(90deg)}.column-drag-handle:hover{background-color:var(--grid-surface-container-high);border-right-color:var(--grid-outline)}.column-header:hover .column-drag-handle{opacity:1}.column-drag-handle:active{cursor:grabbing}.dragging{opacity:1;background-color:var(--grid-surface-container);box-shadow:var(--grid-elevation-2)}.drag-over{background-color:var(--grid-surface-container);border-color:var(--grid-primary)}.data-cell{background-color:var(--grid-surface);color:var(--grid-on-surface);font-size:var(--grid-font-size-body)}.cell-content{align-items:center}.cell-content .mdc-text-field{padding:0px var(--grid-spacing-xxs)!important}.cell-display-text{align-items:center;padding:0px var(--grid-spacing-xs)}.ghost-loading-row{background-color:transparent}.ghost-cell-container{padding:var(--grid-spacing-sm)}.ghost-cell{height:20px;width:100%;background-color:var(--grid-surface-container);animation:pulse 1.5s ease-in-out infinite;border-radius:var(--grid-border-radius)}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.resizing{cursor:col-resize;-webkit-user-select:none;user-select:none}.column-resizer{position:absolute;right:0;top:0;bottom:0;width:4px;cursor:col-resize;background-color:transparent;transition:background-color .2s ease}.column-resizer:hover{background-color:var(--grid-primary)}.group-separator{height:var(--grid-spacing-sm);background-color:var(--grid-surface-variant)}.group-separator .separator-cell{background-color:var(--grid-surface-variant);border:none;height:var(--grid-spacing-sm)}.error-state{background-color:var(--grid-error-container);color:var(--grid-error);border-color:var(--grid-error)}.error-message{background-color:var(--grid-error);color:#fff;padding:var(--grid-spacing-sm);border-radius:var(--grid-border-radius);font-size:var(--grid-font-size-caption)}.incremental-row-container .eru-grid-table tbody,.incremental-row-container .eru-grid-table{position:relative}.incremental-row-container .eru-grid-table.show-column-lines{border-right:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important;border-top:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important;border-bottom:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important}.incremental-row-container .eru-grid-table:not(.show-column-lines){border:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important}.incremental-row-container .eru-grid-table thead:after{content:\"\";position:absolute;bottom:0;left:0;right:0;height:calc(var(--grid-outline-width, 1px) * 2);background-color:var(--grid-outline, #e0e0e0);pointer-events:none;z-index:10}.incremental-row-container .eru-grid-table.show-column-lines thead th{border-left:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important;border-bottom:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important}.incremental-row-container .eru-grid-table.show-column-lines tbody td{border-left:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important}.incremental-row-container .eru-grid-table.show-row-lines thead th{border-left:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important;border-bottom:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important}.incremental-row-container .eru-grid-table.show-row-lines tbody td{border-bottom:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important}@media (max-width: 768px){.incremental-row-container{height:600px}.eru-grid-table th,.eru-grid-table td{font-size:var(--grid-font-size-caption)}.checkbox-column{width:40px;min-width:40px;max-width:40px}}@media (prefers-contrast: high){.eru-grid-table th,.eru-grid-table td{border-width:2px}.row-item:hover{border-width:2px;border-color:var(--grid-primary)}}@media (prefers-reduced-motion: reduce){.row-item,.column-drag-handle,.ghost-cell{transition:none;animation:none}}.pivot-table .nested-header{text-align:center;font-weight:600;background:var(--grid-surface-container)}.pivot-table .nested-header.row-dimension-header{background:var(--grid-surface-container);font-weight:600}.pivot-table .pivot-header-leafcols{padding:0;margin:0;height:0}.pivot-table .pivot-header-level.level-0 .nested-header{font-size:14px;padding:12px 8px}.pivot-table .pivot-header-level.level-1 .nested-header{font-size:13px;padding:10px 6px}.pivot-table .pivot-header-level.level-2 .nested-header{font-size:12px;padding:8px 4px}.pivot-table .nested-header:hover{background:var(--grid-surface-variant);color:var(--grid-primary);transition:all .2s ease}.pivot-table .pivot-cell.aggregated-value{font-weight:500;font-family:Roboto Mono,monospace}.pivot-table .pivot-cell-content{display:flex;justify-content:center;align-items:center;min-height:38px}.pivot-table .rowspan-cell{vertical-align:middle}.pivot-table .rowspan-cell .pivot-cell-content{height:100%}.pivot-mode .incremental-row-container{display:flex;flex-direction:column;height:auto;max-height:85vh;overflow:auto}.pivot-mode .h-shell{position:relative;width:calc(100% - var(--scrollbar-width, 17px))!important;top:0;z-index:1;overflow-x:hidden;overflow-y:hidden;scrollbar-width:none;-ms-overflow-style:none}.pivot-mode .h-shell::-webkit-scrollbar{display:none}.pivot-mode .gt-shell{position:relative;bottom:50px;flex-shrink:0;overflow-x:hidden;overflow-y:hidden;scrollbar-width:none;-ms-overflow-style:none}.pivot-mode .gt-shell::-webkit-scrollbar{display:none}.pivot-mode .gt-shell table{border-bottom:var(--grid-outline-width, 1px) solid var(--grid-outline, #e0e0e0)!important}.pivot-mode .gt-shell.adjust-bottom-vs{bottom:66px!important}.pivot-mode .gt-shell.adjust-bottom:not(.adjust-bottom-vs){bottom:calc(66px - var(--scrollbar-width, 17px))!important}.pivot-mode .header-shell{flex-shrink:0;width:100%;box-sizing:border-box;padding-right:var(--scrollbar-width, 17px);overflow-x:auto;overflow-y:hidden;scrollbar-width:none;-ms-overflow-style:none}.pivot-mode .header-shell::-webkit-scrollbar{display:none}.pivot-mode .header-shell.apply-cdk-width{width:calc(var(--table-total-width) + 10px)!important}.pivot-mode .header-shell .eru-grid-table{margin-bottom:0;width:100%;table-layout:fixed}.pivot-mode .header-shell .eru-grid-table thead{background:var(--grid-surface-container)}.pivot-mode .header-shell .eru-grid-table thead th{background:var(--grid-surface-container);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:var(--col-width)!important;min-width:var(--col-width)!important;max-width:var(--col-width)!important}.pivot-mode .header-shell .eru-grid-table thead th.sticky-column{position:sticky;background:var(--grid-surface-container);z-index:111}.pivot-mode .header-shell .eru-grid-table tbody td{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:var(--col-width)!important;min-width:var(--col-width)!important;max-width:var(--col-width)!important}.pivot-mode .pivot-container{display:flex;flex-direction:column;height:100%;width:100%;overflow:hidden}.pivot-mode .pivot-table{width:auto!important;min-width:100%!important;table-layout:fixed!important;width:100%!important}.pivot-mode .pivot-table colgroup col{width:var(--col-width)!important;min-width:var(--col-width)!important;max-width:var(--col-width)!important;flex:none!important;flex-shrink:0!important;flex-grow:0!important}.pivot-mode .pivot-table td,.pivot-mode .pivot-table th{box-sizing:border-box!important;flex:none!important;flex-shrink:0!important;flex-grow:0!important;word-wrap:break-word!important;word-break:break-all!important}.pivot-mode .pivot-table *{max-width:var(--col-width)!important;box-sizing:border-box!important}.pivot-mode .pivot-table colgroup{width:100%!important}.pivot-mode .pivot-table colgroup col{width:var(--col-width)!important;min-width:var(--col-width)!important;max-width:var(--col-width)!important;flex-basis:var(--col-width)!important;flex:0 0 var(--col-width)!important}.pivot-mode .pivot-table table{width:100%!important;table-layout:fixed!important;border-collapse:collapse!important;border-spacing:0!important}.pivot-mode .pivot-table[style*=--table-total-width]{width:var(--table-total-width)!important;min-width:var(--table-total-width)!important;max-width:var(--table-total-width)!important}.pivot-mode .pivot-table colgroup col{width:var(--col-width)!important;min-width:var(--col-width)!important;max-width:var(--col-width)!important;flex:0 0 var(--col-width)!important;flex-basis:var(--col-width)!important;flex-grow:0!important;flex-shrink:0!important;overflow:hidden!important}.pivot-mode .pivot-table tbody td,.pivot-mode .pivot-table thead th{width:var(--col-width)!important;min-width:var(--col-width)!important;max-width:var(--col-width)!important;overflow:hidden!important;text-overflow:ellipsis!important;white-space:nowrap!important}.pivot-mode .pivot-table .cell-content,.pivot-mode .pivot-table data-cell{width:100%!important;max-width:100%!important;overflow:hidden!important;text-overflow:ellipsis!important;white-space:nowrap!important;display:block!important}.pivot-mode .pivot-table table{width:var(--table-total-width)!important;min-width:var(--table-total-width)!important;max-width:var(--table-total-width)!important;table-layout:fixed!important;border-collapse:collapse!important;border-spacing:0!important;word-wrap:break-word!important;word-break:break-all!important}.pivot-mode .pivot-tbody tr.pivot-row{min-height:50px!important;height:50px!important}.pivot-mode .pivot-tbody tr.pivot-row:hover{background-color:var(--grid-surface-variant)}.pivot-mode .pivot-tbody tr.pivot-row:nth-child(2n){background-color:#00000005}.pivot-mode .pivot-tbody tr.pivot-row td{min-height:50px!important;height:50px!important;vertical-align:middle;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:var(--col-width)!important;min-width:var(--col-width)!important;max-width:var(--col-width)!important}.pivot-mode .pivot-tbody tr.pivot-row td .cell-content{min-height:48px;display:flex;align-items:center;justify-content:center}.pivot-mode .pivot-tbody tr.pivot-row td .cell-content data-cell{width:100%;min-height:46px;display:flex;align-items:center;justify-content:center;overflow:hidden;flex-shrink:0}.pivot-mode .pivot-cell{vertical-align:middle;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:var(--col-width)!important;min-width:var(--col-width)!important;max-width:var(--col-width)!important}.pivot-mode .pivot-cell.aggregated-value{font-weight:500;font-family:Roboto Mono,monospace}.pivot-mode .pivot-cell .cell-content{display:flex;justify-content:center;align-items:center;min-height:40px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex-shrink:0}.pivot-mode .pivot-table .subtotal-row{background-color:var(--grid-surface-container)!important;font-weight:600}.pivot-mode .pivot-table .subtotal-row td{background-color:var(--grid-surface-container);color:var(--grid-on-surface-variant)}.pivot-mode .pivot-table .subtotal-row td:first-child{color:var(--grid-primary)}.pivot-mode .pivot-table .subtotal-row td.aggregated-value{font-weight:500;color:var(--grid-primary)}.pivot-mode .pivot-table .subtotal-row:hover{background-color:var(--grid-surface-container-high)!important}.pivot-mode .pivot-table .subtotal-row:hover td{background-color:var(--grid-surface-container-high)}.pivot-mode .pivot-table .subtotal-bold td{font-weight:600!important;font-style:normal!important}.pivot-mode .pivot-table .subtotal-bold td.aggregated-value{font-weight:600!important}.pivot-mode .pivot-table .subtotal-italic td{font-style:italic!important}.pivot-mode .pivot-table .subtotal-italic td:first-child{font-weight:600!important}.pivot-mode .pivot-table .subtotal-italic td.aggregated-value{font-style:italic!important;font-weight:500!important}.pivot-mode .pivot-table .subtotal-highlighted{background-color:var(--grid-surface-variant)!important}.pivot-mode .pivot-table .subtotal-highlighted td{background-color:var(--grid-surface-variant)!important;font-weight:700!important;font-style:normal!important;color:var(--grid-primary)!important}.pivot-mode .pivot-table .subtotal-highlighted td.aggregated-value{font-weight:500!important;color:var(--grid-primary)!important}.pivot-mode .pivot-table .subtotal-highlighted:hover,.pivot-mode .pivot-table .subtotal-highlighted:hover td{background-color:var(--grid-surface-container-high)!important}.pivot-mode .pivot-table .grand-total-row{background-color:var(--grid-surface-container-high)!important;font-weight:700;font-size:var(--grid-font-size-body)}.pivot-mode .pivot-table .grand-total-row td{background-color:var(--grid-surface-container-high)!important;color:var(--grid-on-surface)}.pivot-mode .pivot-table .grand-total-row td:first-child{font-style:normal;font-weight:800;color:var(--grid-primary)}.pivot-mode .pivot-table .grand-total-row td.aggregated-value{font-weight:500;color:var(--grid-primary);font-family:Roboto Mono,monospace}.pivot-mode .pivot-table .grand-total-row:hover,.pivot-mode .pivot-table .grand-total-row:hover td{background-color:var(--grid-surface-container-high)!important}.pivot-mode .pivot-table .grand-total-bold td{font-weight:700!important;font-style:normal!important}.pivot-mode .pivot-table .grand-total-bold td.aggregated-value{font-weight:700!important}.pivot-mode .pivot-table .grand-total-italic td,.pivot-mode .pivot-table .grand-total-italic td.aggregated-value{font-style:italic!important;font-weight:500!important}.pivot-mode .pivot-table .grand-total-highlighted{background-color:var(--grid-primary)!important;box-shadow:var(--grid-elevation-2)!important}.pivot-mode .pivot-table .grand-total-highlighted td{background-color:var(--grid-primary)!important;color:var(--grid-on-primary)!important;font-weight:500!important;font-style:normal!important}.pivot-mode .pivot-table .grand-total-highlighted td.aggregated-value{color:var(--grid-on-primary)!important;font-weight:500!important}.pivot-mode .pivot-table .grand-total-highlighted:hover,.pivot-mode .pivot-table .grand-total-highlighted:hover td{background-color:var(--grid-primary)!important}.pivot-mode .pivot-table .collapsible-header{position:relative}.pivot-mode .pivot-table .collapsible-header .header-content{display:flex;align-items:center;justify-content:space-between;gap:var(--grid-spacing-xs);padding:var(--grid-spacing-xs) var(--grid-spacing-sm)}.pivot-mode .pivot-table .collapsible-header .header-label{flex:1;font-weight:600}.pivot-mode .pivot-table .collapsible-header .collapse-toggle-btn{background:none;border:none;cursor:pointer;padding:var(--grid-spacing-xxs);margin:0;display:flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:var(--grid-border-radius);color:var(--grid-on-surface-variant);transition:all .2s ease;font-size:12px;font-weight:600}.pivot-mode .pivot-table .collapsible-header .collapse-toggle-btn:hover{background-color:var(--grid-surface-container);color:var(--grid-primary);transform:scale(1.1)}.pivot-mode .pivot-table .collapsible-header .collapse-toggle-btn:focus{outline:2px solid var(--grid-primary);outline-offset:1px}.pivot-mode .pivot-table .collapsible-header .collapse-toggle-btn .collapse-icon{display:block;line-height:1;font-family:monospace;font-size:14px}.pivot-mode .pivot-table .collapsible-header.expanded .collapse-toggle-btn .collapse-icon{color:var(--grid-primary)}.pivot-mode .pivot-table .collapsible-header.collapsed{background-color:var(--grid-surface-variant)}.pivot-mode .pivot-table .collapsible-header.collapsed .header-label{font-style:italic;color:var(--grid-on-surface-variant)}.pivot-mode .pivot-table .collapsible-header.collapsed .collapse-toggle-btn .collapse-icon{color:var(--grid-outline)}.pivot-mode .pivot-table .collapsible-header:hover{background-color:var(--grid-surface-container)}.pivot-mode .pivot-table .collapsible-header:hover .header-label{color:var(--grid-on-surface)}.pivot-mode .pivot-table .pivot-single-table{display:flex;flex-direction:column;height:100%;width:100%;overflow:hidden;min-height:var(--table-min-height)!important}.pivot-mode .pivot-table .pivot-single-table .pivot-header-container{flex-shrink:0;background:var(--grid-surface)!important;overflow-x:auto;overflow-y:hidden;min-height:100px!important;height:auto!important}.pivot-mode .pivot-table .pivot-single-table .pivot-header-container .pivot-table{width:auto;min-width:100%;height:auto!important;min-height:100px!important}.pivot-mode .pivot-table .pivot-single-table .pivot-header-container .pivot-table th{background:var(--grid-surface-container)!important;padding:8px 6px!important;white-space:nowrap;min-width:50px;min-height:40px!important;height:auto!important;position:relative;visibility:visible!important;color:var(--grid-on-surface)!important}.pivot-mode .pivot-table .pivot-single-table .pivot-header-container .pivot-table th.sticky-column{position:sticky!important;background:var(--grid-surface-container)!important;border-right:2px solid var(--grid-primary)!important;box-shadow:2px 0 4px #0000001a;z-index:101!important}.pivot-mode .pivot-table .pivot-single-table .pivot-data-container{flex:1;overflow:auto;min-height:300px!important;height:auto!important}.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-viewport{height:100%!important;width:100%!important;overflow-x:auto!important;overflow-y:auto!important}.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-table,.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-data-table{width:auto;min-width:100%;height:auto!important}.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-table td,.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-data-table td{padding:8px 6px!important;white-space:nowrap;min-width:50px;min-height:32px!important;height:auto!important;background:var(--grid-surface)!important;color:var(--grid-on-surface)!important;visibility:visible!important}.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-table td.sticky-column,.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-data-table td.sticky-column{position:sticky!important;background:var(--grid-surface-container)!important;border-right:2px solid var(--grid-primary)!important;box-shadow:2px 0 4px #0000001a;z-index:100!important}.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-table tbody tr,.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-data-table tbody tr{height:auto!important;min-height:50px!important}.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-table tbody tr.pivot-row,.pivot-mode .pivot-table .pivot-single-table .pivot-data-container .pivot-data-table tbody tr.pivot-row{visibility:visible!important;display:table-row!important}.pivot-mode .pivot-table .collapsed-column-group{background-color:var(--grid-surface-container);border-left:3px solid var(--grid-primary)}.pivot-mode .pivot-table .collapsed-column-group:hover{background-color:var(--grid-surface-container-high)}.pivot-row.subtotal-row{background-color:var(--grid-surface-variant);font-weight:500}.pivot-row.subtotal-row.subtotal-bold{font-weight:500}.pivot-row.subtotal-row.subtotal-italic{font-style:italic}.pivot-row.subtotal-row.subtotal-highlighted{background-color:var(--grid-primary);color:var(--grid-on-primary)}.pivot-row.grand-total-row{background-color:var(--grid-surface-container);font-weight:600}.pivot-row.grand-total-row.grand-total-bold{font-weight:800}.pivot-row.grand-total-row.grand-total-italic{font-style:italic}.pivot-row.grand-total-row.grand-total-highlighted{background-color:var(--grid-primary);color:var(--grid-on-primary)}.pivot-row.first-visible-row{background-color:#6750a41a!important;position:relative}.pivot-row.first-visible-row:before{content:\"\\1f441\\fe0f First Visible\";position:absolute;top:-20px;left:0;background:var(--grid-primary);color:var(--grid-on-primary);padding:2px 6px;font-size:10px;border-radius:2px;z-index:1000}.header-wrap-text{white-space:pre-wrap;word-break:auto-phrase}\n"] }]
6439
5776
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { viewport: [{
6440
5777
  type: ViewChild,
6441
5778
  args: [CdkVirtualScrollViewport]