eru-grid 0.0.14 → 0.0.16
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.
- package/fesm2022/eru-grid.mjs +791 -286
- package/fesm2022/eru-grid.mjs.map +1 -1
- package/index.d.ts +78 -12
- package/package.json +1 -1
package/fesm2022/eru-grid.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Injectable, inject, signal, computed, DOCUMENT, effect, Directive, InjectionToken, forwardRef, Input, Component, ElementRef,
|
|
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
3
|
import { of, delay } from 'rxjs';
|
|
4
4
|
import * as i2$1 from '@angular/common';
|
|
5
5
|
import { NgFor, NgIf, NgClass, DatePipe, CommonModule } from '@angular/common';
|
|
@@ -114,7 +114,6 @@ class PivotTransformService {
|
|
|
114
114
|
const groupedData = this.groupByDimensions(sourceData, rowDimensionFields, columnDimensionFields);
|
|
115
115
|
// Step 3: Use pre-collected unique column values
|
|
116
116
|
const pivotColumns = groupedData.uniqueColumnValues;
|
|
117
|
-
console.log('pivotColumns', pivotColumns);
|
|
118
117
|
// Step 4: Create pivot rows with aggregated data
|
|
119
118
|
let pivotRows = this.createPivotRows(groupedData.groupedData, pivotColumns, configuration);
|
|
120
119
|
// Step 4a: Add subtotals and grand totals if configured
|
|
@@ -406,25 +405,30 @@ class PivotTransformService {
|
|
|
406
405
|
});
|
|
407
406
|
return Array.from(values).sort();
|
|
408
407
|
});
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
408
|
+
// Generate subtotals for EACH level of column dimensions separately
|
|
409
|
+
// This creates subtotals for Priority, Rating, etc. individually
|
|
410
|
+
for (let dimIndex = 0; dimIndex < subtotalDimensions.length; dimIndex++) {
|
|
411
|
+
const currentDimension = subtotalDimensions[dimIndex];
|
|
412
|
+
const currentDimensionValues = dimensionValues[dimIndex];
|
|
413
|
+
// For each value in this dimension
|
|
414
|
+
currentDimensionValues.forEach(dimValue => {
|
|
415
|
+
aggregations.forEach(aggregation => {
|
|
416
|
+
if (aggregation.showSubtotals) {
|
|
417
|
+
// Find the column config for this aggregation
|
|
418
|
+
const aggregationColumn = fields.find(field => field.name === aggregation.name);
|
|
419
|
+
const columnName = `_${dimValue}_subtotal_${aggregation.name}`;
|
|
420
|
+
columns.push({
|
|
421
|
+
name: columnName,
|
|
422
|
+
label: `${dimValue} Subtotal - ${aggregation.label || aggregation.name}`,
|
|
423
|
+
datatype: this.getAggregationDataType(aggregation.aggregationFunction),
|
|
424
|
+
field_size: aggregation.field_size || aggregationColumn?.field_size || 120,
|
|
425
|
+
grid_index: columnIndex++,
|
|
426
|
+
enableDrilldown: aggregation.enableDrilldown || false
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
});
|
|
426
430
|
});
|
|
427
|
-
}
|
|
431
|
+
}
|
|
428
432
|
}
|
|
429
433
|
}
|
|
430
434
|
// Add column grand total columns if enabled (before pivot columns)
|
|
@@ -466,30 +470,34 @@ class PivotTransformService {
|
|
|
466
470
|
if (enableColumnSubtotals && subtotalPositionColumn === 'after') {
|
|
467
471
|
// Only add subtotals for dimensions that are not the last column dimension
|
|
468
472
|
if (columnDimensions && columnDimensions.length > 1) {
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
const dimensionValues = subtotalDimensions.map((dimension, dimIndex) => {
|
|
473
|
+
// Extract unique values for each column dimension
|
|
474
|
+
const dimensionValues = columnDimensions.map((dimension, dimIndex) => {
|
|
472
475
|
const values = new Set();
|
|
476
|
+
// Extract unique values from pivot column names
|
|
473
477
|
pivotColumns.forEach(pivotColumn => {
|
|
474
478
|
if (pivotColumn !== '_default') {
|
|
475
479
|
const parts = pivotColumn.split('|');
|
|
476
|
-
if (parts[dimIndex]) {
|
|
480
|
+
if (parts[dimIndex] !== undefined) {
|
|
477
481
|
values.add(parts[dimIndex]);
|
|
478
482
|
}
|
|
479
483
|
}
|
|
480
484
|
});
|
|
481
485
|
return Array.from(values).sort();
|
|
482
486
|
});
|
|
483
|
-
|
|
487
|
+
// Generate hierarchical subtotal combinations
|
|
488
|
+
const subtotalCombinations = this.generateSubtotalCombinations(dimensionValues, columnDimensions.length - 1);
|
|
484
489
|
subtotalCombinations.forEach(combination => {
|
|
485
|
-
const
|
|
490
|
+
const { dimensionPath, level } = combination;
|
|
486
491
|
aggregations.forEach(aggregation => {
|
|
487
492
|
if (aggregation.showSubtotals) {
|
|
488
493
|
const aggregationColumn = fields.find(field => field.name === aggregation.name);
|
|
489
|
-
const
|
|
494
|
+
const subtotalColumnName = `_${dimensionPath.join('_')}_subtotal_${aggregation.name}`;
|
|
495
|
+
const subtotalLabel = level === 0
|
|
496
|
+
? `${dimensionPath[0]} Subtotal - ${aggregation.label || aggregation.name}`
|
|
497
|
+
: `${dimensionPath.join('+')} Subtotal - ${aggregation.label || aggregation.name}`;
|
|
490
498
|
columns.push({
|
|
491
|
-
name:
|
|
492
|
-
label:
|
|
499
|
+
name: subtotalColumnName,
|
|
500
|
+
label: subtotalLabel,
|
|
493
501
|
datatype: this.getAggregationDataType(aggregation.aggregationFunction),
|
|
494
502
|
field_size: aggregation.field_size || aggregationColumn?.field_size || 120,
|
|
495
503
|
grid_index: columnIndex++,
|
|
@@ -575,7 +583,7 @@ class PivotTransformService {
|
|
|
575
583
|
generateNestedHeaderStructure(rowDimensions, columnDimensions, aggregations, sourceData, gridConfiguration) {
|
|
576
584
|
// Use flat structure only if no column dimensions AND single aggregation
|
|
577
585
|
if (columnDimensions.length === 0 && aggregations.length <= 1) {
|
|
578
|
-
return this.generateFlatHeaderStructure(rowDimensions, columnDimensions, aggregations, sourceData);
|
|
586
|
+
return this.generateFlatHeaderStructure(rowDimensions, columnDimensions, aggregations, sourceData, gridConfiguration);
|
|
579
587
|
}
|
|
580
588
|
// Get unique values for each column dimension
|
|
581
589
|
const dimensionValues = columnDimensions.map(dimension => {
|
|
@@ -589,14 +597,9 @@ class PivotTransformService {
|
|
|
589
597
|
// Build hierarchical header structure
|
|
590
598
|
const headerRows = [];
|
|
591
599
|
const maxDepth = columnDimensions.length + (aggregations.length > 1 ? 1 : 0);
|
|
592
|
-
console.log('Header structure initialization:');
|
|
593
|
-
console.log('columnDimensions:', columnDimensions);
|
|
594
|
-
console.log('aggregations:', aggregations);
|
|
595
|
-
console.log('maxDepth:', maxDepth);
|
|
596
600
|
// Initialize header rows
|
|
597
601
|
for (let i = 0; i < maxDepth; i++) {
|
|
598
602
|
headerRows.push([]);
|
|
599
|
-
console.log(`Initialized headerRows[${i}] with length:`, headerRows[i].length);
|
|
600
603
|
}
|
|
601
604
|
// Add row dimension headers (they span all header rows)
|
|
602
605
|
const fields = gridConfiguration?.fields || [];
|
|
@@ -673,31 +676,13 @@ class PivotTransformService {
|
|
|
673
676
|
const dimension = columnDimensions[currentLevel];
|
|
674
677
|
const groupKey = this.generateColumnGroupKey(dimension, currentLevel);
|
|
675
678
|
const isExpanded = this.isColumnGroupExpanded(groupKey);
|
|
676
|
-
//
|
|
677
|
-
let parentValue = undefined;
|
|
678
|
-
if (currentLevel > 0) {
|
|
679
|
-
// For levels beyond the first, determine the parent combination values
|
|
680
|
-
const parentDimensionIndex = Math.floor(parentIndex /
|
|
681
|
-
dimensionValues.slice(currentLevel).reduce((acc, values) => acc * values.length, 1));
|
|
682
|
-
const parentDimensionValues = [];
|
|
683
|
-
// Build parent path by working backwards through dimensions
|
|
684
|
-
let tempIndex = parentIndex;
|
|
685
|
-
for (let level = currentLevel - 1; level >= 0; level--) {
|
|
686
|
-
const levelSize = dimensionValues.slice(level + 1, currentLevel).reduce((acc, values) => acc * values.length, 1);
|
|
687
|
-
const levelIndex = Math.floor(tempIndex / levelSize);
|
|
688
|
-
parentDimensionValues.unshift(dimensionValues[level][levelIndex % dimensionValues[level].length]);
|
|
689
|
-
tempIndex = tempIndex % levelSize;
|
|
690
|
-
}
|
|
691
|
-
parentValue = parentDimensionValues.join('+');
|
|
692
|
-
}
|
|
693
|
-
// Add the header with parentValue for N-dimensional support
|
|
679
|
+
// Add the priority header
|
|
694
680
|
headerRows[currentLevel].push({
|
|
695
681
|
name: dimension,
|
|
696
682
|
label: value,
|
|
697
683
|
level: currentLevel,
|
|
698
684
|
colspan,
|
|
699
685
|
rowspan,
|
|
700
|
-
parentValue,
|
|
701
686
|
// Collapsible properties
|
|
702
687
|
isCollapsible: true,
|
|
703
688
|
isExpanded: isExpanded,
|
|
@@ -708,16 +693,13 @@ class PivotTransformService {
|
|
|
708
693
|
// If this is the last dimension and we have multiple aggregations,
|
|
709
694
|
// add aggregation headers for this dimension value
|
|
710
695
|
if (isLastDimension && hasMultipleAggregations) {
|
|
711
|
-
// For aggregation headers, the parentValue includes the current dimension value
|
|
712
|
-
const aggParentValue = parentValue ? `${parentValue}+${value}` : value;
|
|
713
696
|
aggregations.forEach(agg => {
|
|
714
697
|
headerRows[currentLevel + 1].push({
|
|
715
698
|
name: agg.name,
|
|
716
699
|
label: agg.label || agg.name,
|
|
717
700
|
level: currentLevel + 1,
|
|
718
701
|
colspan: 1,
|
|
719
|
-
rowspan: 1
|
|
720
|
-
parentValue: aggParentValue
|
|
702
|
+
rowspan: 1
|
|
721
703
|
});
|
|
722
704
|
});
|
|
723
705
|
}
|
|
@@ -729,38 +711,46 @@ class PivotTransformService {
|
|
|
729
711
|
}
|
|
730
712
|
}
|
|
731
713
|
/**
|
|
732
|
-
* Calculate colspan for a header at given level
|
|
714
|
+
* Calculate colspan for a header at given level - GENERIC VERSION
|
|
733
715
|
*/
|
|
734
716
|
calculateColspan(dimensionValues, currentLevel, aggregationCount, isColumnSubtotalsEnabled, subtotalPositionColumn, aggregations) {
|
|
735
|
-
|
|
736
|
-
|
|
717
|
+
// If subtotals are not enabled, use original logic
|
|
718
|
+
if (!isColumnSubtotalsEnabled || dimensionValues.length <= 1) {
|
|
719
|
+
let colspan = 1;
|
|
720
|
+
// Multiply by values in deeper levels
|
|
721
|
+
for (let i = currentLevel + 1; i < dimensionValues.length; i++) {
|
|
722
|
+
colspan *= dimensionValues[i].length;
|
|
723
|
+
}
|
|
724
|
+
// Multiply by aggregation count if multiple aggregations
|
|
725
|
+
if (aggregationCount > 1) {
|
|
726
|
+
colspan *= aggregationCount;
|
|
727
|
+
}
|
|
728
|
+
return colspan;
|
|
729
|
+
}
|
|
730
|
+
// With subtotals enabled, calculate based on interleaved structure - GENERIC
|
|
731
|
+
const subtotalAggregations = aggregations.filter(agg => agg.showSubtotals);
|
|
732
|
+
let colspan = 0;
|
|
733
|
+
// Calculate child columns recursively for all remaining levels
|
|
734
|
+
let childColumns = aggregationCount; // Base aggregation columns
|
|
737
735
|
for (let i = currentLevel + 1; i < dimensionValues.length; i++) {
|
|
738
|
-
|
|
739
|
-
}
|
|
740
|
-
//
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
const subtotalAggregations = aggregations.filter(agg => agg.showSubtotals);
|
|
748
|
-
// For this level: add subtotal columns for this dimension combination
|
|
749
|
-
// For deeper levels: add subtotal columns for all combinations at those levels
|
|
750
|
-
let subtotalColumns = 0;
|
|
751
|
-
// Add subtotal columns for current level (single dimension combination)
|
|
752
|
-
subtotalColumns += subtotalAggregations.length;
|
|
753
|
-
// Add subtotal columns for deeper level combinations
|
|
754
|
-
for (let level = currentLevel + 1; level < dimensionValues.length - 1; level++) {
|
|
755
|
-
// Calculate combinations at this deeper level that would be under current header
|
|
756
|
-
let deeperCombinations = 1;
|
|
757
|
-
for (let i = currentLevel; i <= level; i++) {
|
|
758
|
-
deeperCombinations *= dimensionValues[i].length;
|
|
759
|
-
}
|
|
760
|
-
subtotalColumns += deeperCombinations * subtotalAggregations.length;
|
|
736
|
+
childColumns *= dimensionValues[i].length;
|
|
737
|
+
}
|
|
738
|
+
// Calculate subtotal columns for each level below current level (not including final level)
|
|
739
|
+
let subtotalColumns = 0;
|
|
740
|
+
for (let subtotalLevel = currentLevel + 1; subtotalLevel < dimensionValues.length - 1; subtotalLevel++) {
|
|
741
|
+
// Count unique combinations of all levels from currentLevel+1 to subtotalLevel (inclusive)
|
|
742
|
+
let combinations = 1;
|
|
743
|
+
for (let i = currentLevel + 1; i <= subtotalLevel; i++) {
|
|
744
|
+
combinations *= dimensionValues[i].length;
|
|
761
745
|
}
|
|
762
|
-
|
|
746
|
+
subtotalColumns += combinations * subtotalAggregations.length;
|
|
763
747
|
}
|
|
748
|
+
// Add own subtotal for this level (except final level)
|
|
749
|
+
let ownSubtotal = 0;
|
|
750
|
+
if (currentLevel < dimensionValues.length - 1) {
|
|
751
|
+
ownSubtotal = subtotalAggregations.length;
|
|
752
|
+
}
|
|
753
|
+
colspan = childColumns + subtotalColumns + ownSubtotal;
|
|
764
754
|
return colspan;
|
|
765
755
|
}
|
|
766
756
|
/**
|
|
@@ -886,10 +876,6 @@ class PivotTransformService {
|
|
|
886
876
|
const aggregationColumn = gridConfiguration?.fields.find((col) => col.name === aggregation.name);
|
|
887
877
|
const columnKey = combination.join('|');
|
|
888
878
|
const columnName = `${columnKey}_${aggregation.name}`;
|
|
889
|
-
console.log('aggregation', aggregation);
|
|
890
|
-
console.log('aggregationColumn', aggregationColumn);
|
|
891
|
-
console.log('columnKey', columnKey);
|
|
892
|
-
console.log('columnName', columnName);
|
|
893
879
|
const columnLabel = aggregations.length > 1
|
|
894
880
|
? `${combination.join(' - ')} (${aggregation.label})`
|
|
895
881
|
: combination.join(' - ');
|
|
@@ -910,6 +896,47 @@ class PivotTransformService {
|
|
|
910
896
|
});
|
|
911
897
|
}
|
|
912
898
|
});
|
|
899
|
+
// Handle column subtotals positioning if enabled
|
|
900
|
+
const enableColumnSubtotals = gridConfiguration?.config?.enableColumnSubtotals ?? false;
|
|
901
|
+
const subtotalPositionColumn = gridConfiguration?.config?.subtotalPositionColumn ?? 'after';
|
|
902
|
+
// Add column subtotal columns with proper interleaved positioning
|
|
903
|
+
if (enableColumnSubtotals && columnDimensions.length > 1) {
|
|
904
|
+
// Store all regular columns first
|
|
905
|
+
const regularColumns = columns.filter(col => !col.name.includes('_subtotal_'));
|
|
906
|
+
const nonPivotColumns = regularColumns.filter(col => !col.name.includes('|'));
|
|
907
|
+
const pivotColumns = regularColumns.filter(col => col.name.includes('|'));
|
|
908
|
+
// Clear columns array and rebuild with interleaved subtotals
|
|
909
|
+
columns.length = 0;
|
|
910
|
+
columnIndex = 0;
|
|
911
|
+
// Add non-pivot columns first (Location, Email, etc.)
|
|
912
|
+
nonPivotColumns.forEach(col => {
|
|
913
|
+
col.grid_index = columnIndex++;
|
|
914
|
+
columns.push(col);
|
|
915
|
+
});
|
|
916
|
+
// Now add pivot columns with interleaved subtotals
|
|
917
|
+
const interleavedColumns = this.addInterleavedSubtotals(pivotColumns, dimensionValues, aggregations, columnDimensions, gridConfiguration, columnIndex, subtotalPositionColumn);
|
|
918
|
+
columns.push(...interleavedColumns);
|
|
919
|
+
// Add column grand total columns if enabled (after pivot columns) - for subtotal case
|
|
920
|
+
if (enableColumnGrandTotal && grandTotalPositionColumn === 'after') {
|
|
921
|
+
aggregations.forEach(aggregation => {
|
|
922
|
+
const aggregationColumn = gridConfiguration?.fields.find((col) => col.name === aggregation.name);
|
|
923
|
+
columns.push({
|
|
924
|
+
name: `_${aggregation.aggregationFunction}_column_grand_total_${aggregation.name}`,
|
|
925
|
+
label: `${this.toTitleCase(aggregation.aggregationFunction)} of ${aggregation.label || aggregation.name}`,
|
|
926
|
+
datatype: this.getAggregationDataType(aggregation.aggregationFunction),
|
|
927
|
+
field_size: aggregation.field_size || aggregationColumn?.field_size || 120,
|
|
928
|
+
grid_index: columnIndex++,
|
|
929
|
+
isLeaf: true,
|
|
930
|
+
level: 0,
|
|
931
|
+
rowspan: dimensionValues.length + (aggregations.length > 1 ? 1 : 0),
|
|
932
|
+
colspan: 1,
|
|
933
|
+
aggregationFunction: aggregation.aggregationFunction,
|
|
934
|
+
enableDrilldown: aggregation.enableDrilldown || false
|
|
935
|
+
});
|
|
936
|
+
});
|
|
937
|
+
}
|
|
938
|
+
return columns;
|
|
939
|
+
}
|
|
913
940
|
// Add column grand total columns if enabled (after pivot columns)
|
|
914
941
|
if (enableColumnGrandTotal && grandTotalPositionColumn === 'after') {
|
|
915
942
|
aggregations.forEach(aggregation => {
|
|
@@ -951,12 +978,6 @@ class PivotTransformService {
|
|
|
951
978
|
* Generate leaf columns for the actual data
|
|
952
979
|
*/
|
|
953
980
|
generateLeafColumns(rowDimensions, dimensionValues, aggregations, columnDimensions, gridConfiguration) {
|
|
954
|
-
console.log(`DEBUG: generateLeafColumns called with:`, {
|
|
955
|
-
rowDimensions,
|
|
956
|
-
dimensionValuesLength: dimensionValues.length,
|
|
957
|
-
aggregationsLength: aggregations.length,
|
|
958
|
-
columnDimensions
|
|
959
|
-
});
|
|
960
981
|
const columns = [];
|
|
961
982
|
let columnIndex = 0;
|
|
962
983
|
// Add row dimension columns
|
|
@@ -1024,10 +1045,6 @@ class PivotTransformService {
|
|
|
1024
1045
|
});
|
|
1025
1046
|
});
|
|
1026
1047
|
});
|
|
1027
|
-
console.log(`DEBUG: generateLeafColumns generated ${columns.length} columns:`);
|
|
1028
|
-
columns.forEach((col, index) => {
|
|
1029
|
-
console.log(` [${index}] ${col.name} (label: "${col.label}")`);
|
|
1030
|
-
});
|
|
1031
1048
|
return columns;
|
|
1032
1049
|
}
|
|
1033
1050
|
/**
|
|
@@ -1048,32 +1065,6 @@ class PivotTransformService {
|
|
|
1048
1065
|
});
|
|
1049
1066
|
return combinations;
|
|
1050
1067
|
}
|
|
1051
|
-
/**
|
|
1052
|
-
* Generate hierarchical subtotal combinations - creates combinations at each level separately
|
|
1053
|
-
* For 3 dimensions [["Low", "Medium"], ["0", "1"], ["Open", "Closed"]] this generates:
|
|
1054
|
-
* Level 0: ["Low"], ["Medium"] (first dimension only)
|
|
1055
|
-
* Level 1: ["Low", "0"], ["Low", "1"], ["Medium", "0"], ["Medium", "1"] (first + second dimensions)
|
|
1056
|
-
* This ensures subtotals are calculated for ALL dimension levels, not just full combinations
|
|
1057
|
-
*/
|
|
1058
|
-
generateHierarchicalSubtotalCombinations(dimensionValues) {
|
|
1059
|
-
const allCombinations = [];
|
|
1060
|
-
// For each level from 0 to dimensionValues.length - 1 (excluding the last level)
|
|
1061
|
-
for (let level = 0; level < dimensionValues.length; level++) {
|
|
1062
|
-
// Generate combinations up to this level (inclusive)
|
|
1063
|
-
const relevantDimensions = dimensionValues.slice(0, level + 1);
|
|
1064
|
-
if (level === 0) {
|
|
1065
|
-
// Level 0: Individual values from first dimension
|
|
1066
|
-
const levelCombinations = dimensionValues[0].map(value => [value]);
|
|
1067
|
-
allCombinations.push(...levelCombinations);
|
|
1068
|
-
}
|
|
1069
|
-
else {
|
|
1070
|
-
// Higher levels: Cartesian product of dimensions up to current level
|
|
1071
|
-
const levelCombinations = this.generateCombinations(relevantDimensions);
|
|
1072
|
-
allCombinations.push(...levelCombinations);
|
|
1073
|
-
}
|
|
1074
|
-
}
|
|
1075
|
-
return allCombinations;
|
|
1076
|
-
}
|
|
1077
1068
|
/**
|
|
1078
1069
|
* Generate flat header structure for single dimension
|
|
1079
1070
|
*/
|
|
@@ -1215,16 +1206,15 @@ class PivotTransformService {
|
|
|
1215
1206
|
if (configuration.cols.length === 0) {
|
|
1216
1207
|
return pivotRows;
|
|
1217
1208
|
}
|
|
1218
|
-
//
|
|
1209
|
+
// Calculate subtotals for ALL dimensions except the last one
|
|
1219
1210
|
const subtotalDimensions = configuration.cols.slice(0, -1);
|
|
1220
1211
|
if (subtotalDimensions.length === 0) {
|
|
1221
1212
|
return pivotRows;
|
|
1222
1213
|
}
|
|
1223
|
-
// Extract unique values for each column dimension
|
|
1224
|
-
const dimensionValues =
|
|
1214
|
+
// Extract unique values for each column dimension
|
|
1215
|
+
const dimensionValues = configuration.cols.map((dimension, dimIndex) => {
|
|
1225
1216
|
const values = new Set();
|
|
1226
1217
|
// Extract unique values from pivot column names
|
|
1227
|
-
// Pivot columns are named like "Low|0", "Low|1", "Medium|0", etc.
|
|
1228
1218
|
pivotColumns.forEach(pivotColumn => {
|
|
1229
1219
|
if (pivotColumn !== '_default') {
|
|
1230
1220
|
const parts = pivotColumn.split('|');
|
|
@@ -1235,48 +1225,34 @@ class PivotTransformService {
|
|
|
1235
1225
|
});
|
|
1236
1226
|
return Array.from(values).sort();
|
|
1237
1227
|
});
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
// Generate hierarchical subtotal combinations (for each level separately)
|
|
1241
|
-
const subtotalCombinations = this.generateHierarchicalSubtotalCombinations(dimensionValues);
|
|
1242
|
-
console.log('Hierarchical subtotal combinations:', subtotalCombinations);
|
|
1228
|
+
// Generate all possible subtotal combinations for each dimension level
|
|
1229
|
+
const subtotalCombinations = this.generateSubtotalCombinations(dimensionValues, subtotalDimensions.length);
|
|
1243
1230
|
// Add column subtotal columns to each row
|
|
1244
1231
|
const result = [];
|
|
1245
1232
|
for (const row of pivotRows) {
|
|
1246
1233
|
const newRow = { ...row };
|
|
1247
|
-
// For each combination
|
|
1234
|
+
// For each subtotal combination
|
|
1248
1235
|
subtotalCombinations.forEach(combination => {
|
|
1249
|
-
const
|
|
1236
|
+
const { dimensionPath, level } = combination;
|
|
1250
1237
|
// For each aggregation, calculate subtotal for this combination
|
|
1251
1238
|
configuration.aggregations.forEach(aggregation => {
|
|
1252
1239
|
if (aggregation.showSubtotals) {
|
|
1253
|
-
|
|
1240
|
+
// Create subtotal column name based on the dimension path
|
|
1241
|
+
const subtotalColumnName = `_${dimensionPath.join('_')}_subtotal_${aggregation.name}`;
|
|
1254
1242
|
let measureTotal = 0;
|
|
1255
1243
|
const matchedColumns = [];
|
|
1256
|
-
// Find all columns that match this combination and sum their values
|
|
1244
|
+
// Find all columns that match this dimension combination and sum their values
|
|
1257
1245
|
Object.keys(row).forEach(key => {
|
|
1258
1246
|
if (key.endsWith(`_${aggregation.name}`) && typeof row[key] === 'number') {
|
|
1259
|
-
//
|
|
1260
|
-
// The key format is "Priority|Rating_measure" (e.g., "Low|0_number")
|
|
1247
|
+
// The key format is "Priority|Rating|Status_measure" (e.g., "Low|0|Open_number")
|
|
1261
1248
|
const columnKey = key.split('_')[0]; // Get the part before the aggregation name
|
|
1262
|
-
const columnParts = columnKey.split('|');
|
|
1263
|
-
// Check if this column matches the current
|
|
1264
|
-
let isMatch =
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
else {
|
|
1270
|
-
// Multi-dimension: check if first N parts of column match the combination
|
|
1271
|
-
// e.g., for combination ["Low", "0"], check if column "Low|0|Open" starts with "Low|0"
|
|
1272
|
-
if (columnParts.length >= combination.length) {
|
|
1273
|
-
isMatch = true;
|
|
1274
|
-
for (let i = 0; i < combination.length; i++) {
|
|
1275
|
-
if (columnParts[i] !== combination[i]) {
|
|
1276
|
-
isMatch = false;
|
|
1277
|
-
break;
|
|
1278
|
-
}
|
|
1279
|
-
}
|
|
1249
|
+
const columnParts = columnKey.split('|');
|
|
1250
|
+
// Check if this column matches the current dimension path exactly
|
|
1251
|
+
let isMatch = true;
|
|
1252
|
+
for (let i = 0; i <= level; i++) {
|
|
1253
|
+
if (columnParts[i] !== dimensionPath[i]) {
|
|
1254
|
+
isMatch = false;
|
|
1255
|
+
break;
|
|
1280
1256
|
}
|
|
1281
1257
|
}
|
|
1282
1258
|
if (isMatch) {
|
|
@@ -1285,12 +1261,6 @@ class PivotTransformService {
|
|
|
1285
1261
|
}
|
|
1286
1262
|
}
|
|
1287
1263
|
});
|
|
1288
|
-
console.log(`Subtotal for ${combinationKey} - ${aggregation.name}:`, {
|
|
1289
|
-
combinationKey,
|
|
1290
|
-
aggregationName: aggregation.name,
|
|
1291
|
-
matchedColumns,
|
|
1292
|
-
measureTotal
|
|
1293
|
-
});
|
|
1294
1264
|
newRow[subtotalColumnName] = measureTotal;
|
|
1295
1265
|
}
|
|
1296
1266
|
});
|
|
@@ -1299,6 +1269,369 @@ class PivotTransformService {
|
|
|
1299
1269
|
}
|
|
1300
1270
|
return result;
|
|
1301
1271
|
}
|
|
1272
|
+
/**
|
|
1273
|
+
* Generate all possible subtotal combinations for hierarchical column dimensions
|
|
1274
|
+
*/
|
|
1275
|
+
generateSubtotalCombinations(dimensionValues, subtotalLevels) {
|
|
1276
|
+
const combinations = [];
|
|
1277
|
+
// For each level that should have subtotals (0 to subtotalLevels-1)
|
|
1278
|
+
for (let level = 0; level < subtotalLevels; level++) {
|
|
1279
|
+
// Generate all combinations up to this level
|
|
1280
|
+
const pathsAtLevel = this.generateCombinationsAtLevel(dimensionValues, level);
|
|
1281
|
+
pathsAtLevel.forEach(path => {
|
|
1282
|
+
combinations.push({
|
|
1283
|
+
dimensionPath: path,
|
|
1284
|
+
level: level
|
|
1285
|
+
});
|
|
1286
|
+
});
|
|
1287
|
+
}
|
|
1288
|
+
return combinations;
|
|
1289
|
+
}
|
|
1290
|
+
/**
|
|
1291
|
+
* Generate all possible dimension paths at a specific level
|
|
1292
|
+
*/
|
|
1293
|
+
generateCombinationsAtLevel(dimensionValues, targetLevel) {
|
|
1294
|
+
if (targetLevel === 0) {
|
|
1295
|
+
// For level 0, return individual values from first dimension
|
|
1296
|
+
return dimensionValues[0].map(value => [value]);
|
|
1297
|
+
}
|
|
1298
|
+
// For higher levels, generate cartesian product up to targetLevel
|
|
1299
|
+
const relevantDimensions = dimensionValues.slice(0, targetLevel + 1);
|
|
1300
|
+
return this.generateCombinations(relevantDimensions);
|
|
1301
|
+
}
|
|
1302
|
+
/**
|
|
1303
|
+
* Generate columns with properly positioned subtotal columns
|
|
1304
|
+
*/
|
|
1305
|
+
generateColumnsWithInterleavedSubtotals(dimensionValues, aggregations, columnDimensions, gridConfiguration, startingColumnIndex = 0) {
|
|
1306
|
+
const columns = [];
|
|
1307
|
+
let columnIndex = startingColumnIndex;
|
|
1308
|
+
const subtotalPositionColumn = gridConfiguration?.config?.subtotalPositionColumn ?? 'after';
|
|
1309
|
+
// Generate all pivot combinations in proper order
|
|
1310
|
+
const allCombinations = this.generateCombinations(dimensionValues);
|
|
1311
|
+
// Group by first level (Priority: Low, Medium, Urgent)
|
|
1312
|
+
const priorityGroups = new Map();
|
|
1313
|
+
allCombinations.forEach(combination => {
|
|
1314
|
+
const priority = combination[0];
|
|
1315
|
+
if (!priorityGroups.has(priority)) {
|
|
1316
|
+
priorityGroups.set(priority, []);
|
|
1317
|
+
}
|
|
1318
|
+
priorityGroups.get(priority).push(combination);
|
|
1319
|
+
});
|
|
1320
|
+
// Process each priority group
|
|
1321
|
+
Array.from(priorityGroups.keys()).sort().forEach(priority => {
|
|
1322
|
+
const priorityCombinations = priorityGroups.get(priority);
|
|
1323
|
+
// If we have multiple dimension levels, group by second level within this priority
|
|
1324
|
+
if (columnDimensions.length > 2) {
|
|
1325
|
+
const ratingGroups = new Map();
|
|
1326
|
+
priorityCombinations.forEach(combination => {
|
|
1327
|
+
if (combination.length >= 2) {
|
|
1328
|
+
const rating = combination[1];
|
|
1329
|
+
const ratingKey = `${priority}|${rating}`;
|
|
1330
|
+
if (!ratingGroups.has(ratingKey)) {
|
|
1331
|
+
ratingGroups.set(ratingKey, []);
|
|
1332
|
+
}
|
|
1333
|
+
ratingGroups.get(ratingKey).push(combination);
|
|
1334
|
+
}
|
|
1335
|
+
});
|
|
1336
|
+
// Process each rating group within this priority
|
|
1337
|
+
Array.from(ratingGroups.keys()).sort().forEach(ratingKey => {
|
|
1338
|
+
const ratingCombinations = ratingGroups.get(ratingKey);
|
|
1339
|
+
// Add all columns for this rating group
|
|
1340
|
+
ratingCombinations.forEach(combination => {
|
|
1341
|
+
aggregations.forEach(aggregation => {
|
|
1342
|
+
const aggregationColumn = gridConfiguration?.fields.find((col) => col.name === aggregation.name);
|
|
1343
|
+
const columnKey = combination.join('|');
|
|
1344
|
+
const columnName = `${columnKey}_${aggregation.name}`;
|
|
1345
|
+
const columnLabel = aggregations.length > 1
|
|
1346
|
+
? `${combination.join(' - ')} (${aggregation.label})`
|
|
1347
|
+
: combination.join(' - ');
|
|
1348
|
+
columns.push({
|
|
1349
|
+
name: columnName,
|
|
1350
|
+
label: columnLabel,
|
|
1351
|
+
datatype: this.getAggregationDataType(aggregation.aggregationFunction),
|
|
1352
|
+
field_size: aggregation.field_size || aggregationColumn?.field_size || 120,
|
|
1353
|
+
grid_index: columnIndex++,
|
|
1354
|
+
isLeaf: true,
|
|
1355
|
+
level: dimensionValues.length + (aggregations.length > 1 ? 1 : 0),
|
|
1356
|
+
parentPath: combination,
|
|
1357
|
+
rowspan: 1,
|
|
1358
|
+
colspan: 1,
|
|
1359
|
+
aggregationFunction: aggregation.aggregationFunction,
|
|
1360
|
+
enableDrilldown: aggregation.enableDrilldown || false
|
|
1361
|
+
});
|
|
1362
|
+
});
|
|
1363
|
+
});
|
|
1364
|
+
// Add rating subtotal after this rating group
|
|
1365
|
+
if (subtotalPositionColumn === 'after') {
|
|
1366
|
+
const ratingParts = ratingKey.split('|');
|
|
1367
|
+
aggregations.forEach(aggregation => {
|
|
1368
|
+
if (aggregation.showSubtotals) {
|
|
1369
|
+
const aggregationColumn = gridConfiguration?.fields.find((col) => col.name === aggregation.name);
|
|
1370
|
+
const subtotalColumnName = `_${ratingParts.join('_')}_subtotal_${aggregation.name}`;
|
|
1371
|
+
const subtotalLabel = `${ratingParts.join('+')} Subtotal - ${aggregation.label || aggregation.name}`;
|
|
1372
|
+
columns.push({
|
|
1373
|
+
name: subtotalColumnName,
|
|
1374
|
+
label: subtotalLabel,
|
|
1375
|
+
datatype: this.getAggregationDataType(aggregation.aggregationFunction),
|
|
1376
|
+
field_size: aggregation.field_size || aggregationColumn?.field_size || 120,
|
|
1377
|
+
grid_index: columnIndex++,
|
|
1378
|
+
isLeaf: true,
|
|
1379
|
+
level: dimensionValues.length + (aggregations.length > 1 ? 1 : 0),
|
|
1380
|
+
parentPath: ratingParts,
|
|
1381
|
+
rowspan: 1,
|
|
1382
|
+
colspan: 1,
|
|
1383
|
+
aggregationFunction: aggregation.aggregationFunction,
|
|
1384
|
+
enableDrilldown: aggregation.enableDrilldown || false
|
|
1385
|
+
});
|
|
1386
|
+
}
|
|
1387
|
+
});
|
|
1388
|
+
}
|
|
1389
|
+
});
|
|
1390
|
+
}
|
|
1391
|
+
else {
|
|
1392
|
+
// For 2 dimensions, just add all columns for this priority
|
|
1393
|
+
priorityCombinations.forEach(combination => {
|
|
1394
|
+
aggregations.forEach(aggregation => {
|
|
1395
|
+
const aggregationColumn = gridConfiguration?.fields.find((col) => col.name === aggregation.name);
|
|
1396
|
+
const columnKey = combination.join('|');
|
|
1397
|
+
const columnName = `${columnKey}_${aggregation.name}`;
|
|
1398
|
+
const columnLabel = aggregations.length > 1
|
|
1399
|
+
? `${combination.join(' - ')} (${aggregation.label})`
|
|
1400
|
+
: combination.join(' - ');
|
|
1401
|
+
columns.push({
|
|
1402
|
+
name: columnName,
|
|
1403
|
+
label: columnLabel,
|
|
1404
|
+
datatype: this.getAggregationDataType(aggregation.aggregationFunction),
|
|
1405
|
+
field_size: aggregation.field_size || aggregationColumn?.field_size || 120,
|
|
1406
|
+
grid_index: columnIndex++,
|
|
1407
|
+
isLeaf: true,
|
|
1408
|
+
level: dimensionValues.length + (aggregations.length > 1 ? 1 : 0),
|
|
1409
|
+
parentPath: combination,
|
|
1410
|
+
rowspan: 1,
|
|
1411
|
+
colspan: 1,
|
|
1412
|
+
aggregationFunction: aggregation.aggregationFunction,
|
|
1413
|
+
enableDrilldown: aggregation.enableDrilldown || false
|
|
1414
|
+
});
|
|
1415
|
+
});
|
|
1416
|
+
});
|
|
1417
|
+
}
|
|
1418
|
+
// Add priority subtotal after all rating groups for this priority
|
|
1419
|
+
if (subtotalPositionColumn === 'after') {
|
|
1420
|
+
aggregations.forEach(aggregation => {
|
|
1421
|
+
if (aggregation.showSubtotals) {
|
|
1422
|
+
const aggregationColumn = gridConfiguration?.fields.find((col) => col.name === aggregation.name);
|
|
1423
|
+
const subtotalColumnName = `_${priority}_subtotal_${aggregation.name}`;
|
|
1424
|
+
const subtotalLabel = `${priority} Subtotal - ${aggregation.label || aggregation.name}`;
|
|
1425
|
+
columns.push({
|
|
1426
|
+
name: subtotalColumnName,
|
|
1427
|
+
label: subtotalLabel,
|
|
1428
|
+
datatype: this.getAggregationDataType(aggregation.aggregationFunction),
|
|
1429
|
+
field_size: aggregation.field_size || aggregationColumn?.field_size || 120,
|
|
1430
|
+
grid_index: columnIndex++,
|
|
1431
|
+
isLeaf: true,
|
|
1432
|
+
level: dimensionValues.length + (aggregations.length > 1 ? 1 : 0),
|
|
1433
|
+
parentPath: [priority],
|
|
1434
|
+
rowspan: 1,
|
|
1435
|
+
colspan: 1,
|
|
1436
|
+
aggregationFunction: aggregation.aggregationFunction,
|
|
1437
|
+
enableDrilldown: aggregation.enableDrilldown || false
|
|
1438
|
+
});
|
|
1439
|
+
}
|
|
1440
|
+
});
|
|
1441
|
+
}
|
|
1442
|
+
});
|
|
1443
|
+
// Add column grand totals if positioned after
|
|
1444
|
+
const enableColumnGrandTotal = gridConfiguration?.config?.enableColumnGrandTotal ?? false;
|
|
1445
|
+
const grandTotalPositionColumn = gridConfiguration?.config?.grandTotalPositionColumn ?? 'after';
|
|
1446
|
+
if (enableColumnGrandTotal && grandTotalPositionColumn === 'after') {
|
|
1447
|
+
aggregations.forEach(aggregation => {
|
|
1448
|
+
const aggregationColumn = gridConfiguration?.fields.find((col) => col.name === aggregation.name);
|
|
1449
|
+
columns.push({
|
|
1450
|
+
name: `_${aggregation.aggregationFunction}_column_grand_total_${aggregation.name}`,
|
|
1451
|
+
label: `${this.toTitleCase(aggregation.aggregationFunction)} of ${aggregation.label || aggregation.name}`,
|
|
1452
|
+
datatype: this.getAggregationDataType(aggregation.aggregationFunction),
|
|
1453
|
+
field_size: aggregation.field_size || aggregationColumn?.field_size || 120,
|
|
1454
|
+
grid_index: columnIndex++,
|
|
1455
|
+
isLeaf: true,
|
|
1456
|
+
level: 0,
|
|
1457
|
+
rowspan: dimensionValues.length + (aggregations.length > 1 ? 1 : 0),
|
|
1458
|
+
colspan: 1,
|
|
1459
|
+
aggregationFunction: aggregation.aggregationFunction,
|
|
1460
|
+
enableDrilldown: aggregation.enableDrilldown || false
|
|
1461
|
+
});
|
|
1462
|
+
});
|
|
1463
|
+
}
|
|
1464
|
+
return columns;
|
|
1465
|
+
}
|
|
1466
|
+
/**
|
|
1467
|
+
* Group combinations by dimension levels for subtotal insertion
|
|
1468
|
+
*/
|
|
1469
|
+
groupCombinationsByDimensions(allCombinations, maxLevel) {
|
|
1470
|
+
const groupedByLevel = [];
|
|
1471
|
+
// For each level (0 to maxLevel-1)
|
|
1472
|
+
for (let level = 0; level < maxLevel; level++) {
|
|
1473
|
+
const levelGroups = [];
|
|
1474
|
+
const groupMap = new Map();
|
|
1475
|
+
// Group combinations by prefix at this level
|
|
1476
|
+
allCombinations.forEach(combination => {
|
|
1477
|
+
if (combination.length > level) {
|
|
1478
|
+
const prefix = combination.slice(0, level + 1);
|
|
1479
|
+
const prefixKey = prefix.join('|');
|
|
1480
|
+
if (!groupMap.has(prefixKey)) {
|
|
1481
|
+
groupMap.set(prefixKey, []);
|
|
1482
|
+
}
|
|
1483
|
+
groupMap.get(prefixKey).push(combination);
|
|
1484
|
+
}
|
|
1485
|
+
});
|
|
1486
|
+
// Convert map to array format
|
|
1487
|
+
groupMap.forEach((combinations, prefixKey) => {
|
|
1488
|
+
const prefix = prefixKey.split('|');
|
|
1489
|
+
levelGroups.push({ prefix, combinations });
|
|
1490
|
+
});
|
|
1491
|
+
groupedByLevel.push(levelGroups);
|
|
1492
|
+
}
|
|
1493
|
+
return groupedByLevel;
|
|
1494
|
+
}
|
|
1495
|
+
/**
|
|
1496
|
+
* Add interleaved subtotals within column groups
|
|
1497
|
+
*/
|
|
1498
|
+
addInterleavedSubtotals(pivotColumns, dimensionValues, aggregations, columnDimensions, gridConfiguration, startingIndex = 0, subtotalPosition = 'after') {
|
|
1499
|
+
const result = [];
|
|
1500
|
+
let columnIndex = startingIndex;
|
|
1501
|
+
// For now, let's implement a simpler approach - group by Priority first
|
|
1502
|
+
const priorityGroups = new Map();
|
|
1503
|
+
// Group columns by Priority (first dimension)
|
|
1504
|
+
pivotColumns.forEach(column => {
|
|
1505
|
+
const columnKey = column.name.split('_')[0]; // Get part before aggregation
|
|
1506
|
+
const parts = columnKey.split('|');
|
|
1507
|
+
const priority = parts[0]; // Low, Medium, Urgent
|
|
1508
|
+
if (!priorityGroups.has(priority)) {
|
|
1509
|
+
priorityGroups.set(priority, []);
|
|
1510
|
+
}
|
|
1511
|
+
priorityGroups.get(priority).push(column);
|
|
1512
|
+
});
|
|
1513
|
+
// Process each priority group
|
|
1514
|
+
Array.from(priorityGroups.keys()).sort().forEach(priority => {
|
|
1515
|
+
const priorityColumns = priorityGroups.get(priority);
|
|
1516
|
+
// Add priority subtotal before if configured
|
|
1517
|
+
if (subtotalPosition === 'before') {
|
|
1518
|
+
aggregations.forEach(aggregation => {
|
|
1519
|
+
if (aggregation.showSubtotals) {
|
|
1520
|
+
const subtotalColumn = this.createSubtotalColumn([priority], 0, aggregation, gridConfiguration, columnIndex++);
|
|
1521
|
+
result.push(subtotalColumn);
|
|
1522
|
+
}
|
|
1523
|
+
});
|
|
1524
|
+
}
|
|
1525
|
+
// Group priority columns by Rating (second dimension)
|
|
1526
|
+
const ratingGroups = new Map();
|
|
1527
|
+
priorityColumns.forEach(column => {
|
|
1528
|
+
const columnKey = column.name.split('_')[0];
|
|
1529
|
+
const parts = columnKey.split('|');
|
|
1530
|
+
if (parts.length >= 2) {
|
|
1531
|
+
const rating = parts[1]; // 0, 1, 3, 5
|
|
1532
|
+
const ratingKey = `${priority}|${rating}`;
|
|
1533
|
+
if (!ratingGroups.has(ratingKey)) {
|
|
1534
|
+
ratingGroups.set(ratingKey, []);
|
|
1535
|
+
}
|
|
1536
|
+
ratingGroups.get(ratingKey).push(column);
|
|
1537
|
+
}
|
|
1538
|
+
});
|
|
1539
|
+
// Process each rating group within this priority
|
|
1540
|
+
Array.from(ratingGroups.keys()).sort().forEach(ratingKey => {
|
|
1541
|
+
const ratingColumns = ratingGroups.get(ratingKey);
|
|
1542
|
+
const ratingParts = ratingKey.split('|'); // [priority, rating]
|
|
1543
|
+
// Add rating subtotal before if configured
|
|
1544
|
+
if (subtotalPosition === 'before') {
|
|
1545
|
+
aggregations.forEach(aggregation => {
|
|
1546
|
+
if (aggregation.showSubtotals) {
|
|
1547
|
+
const subtotalColumn = this.createSubtotalColumn(ratingParts, 1, aggregation, gridConfiguration, columnIndex++);
|
|
1548
|
+
result.push(subtotalColumn);
|
|
1549
|
+
}
|
|
1550
|
+
});
|
|
1551
|
+
}
|
|
1552
|
+
// Add the actual columns for this rating group
|
|
1553
|
+
ratingColumns.forEach(col => {
|
|
1554
|
+
col.grid_index = columnIndex++;
|
|
1555
|
+
result.push(col);
|
|
1556
|
+
});
|
|
1557
|
+
// Add rating subtotal after if configured
|
|
1558
|
+
if (subtotalPosition === 'after') {
|
|
1559
|
+
aggregations.forEach(aggregation => {
|
|
1560
|
+
if (aggregation.showSubtotals) {
|
|
1561
|
+
const subtotalColumn = this.createSubtotalColumn(ratingParts, 1, aggregation, gridConfiguration, columnIndex++);
|
|
1562
|
+
result.push(subtotalColumn);
|
|
1563
|
+
}
|
|
1564
|
+
});
|
|
1565
|
+
}
|
|
1566
|
+
});
|
|
1567
|
+
// Add priority subtotal after if configured
|
|
1568
|
+
if (subtotalPosition === 'after') {
|
|
1569
|
+
aggregations.forEach(aggregation => {
|
|
1570
|
+
if (aggregation.showSubtotals) {
|
|
1571
|
+
const subtotalColumn = this.createSubtotalColumn([priority], 0, aggregation, gridConfiguration, columnIndex++);
|
|
1572
|
+
result.push(subtotalColumn);
|
|
1573
|
+
}
|
|
1574
|
+
});
|
|
1575
|
+
}
|
|
1576
|
+
});
|
|
1577
|
+
return result;
|
|
1578
|
+
}
|
|
1579
|
+
/**
|
|
1580
|
+
* Group pivot columns by dimension prefixes for subtotal insertion
|
|
1581
|
+
*/
|
|
1582
|
+
groupPivotColumnsByDimensions(pivotColumns, maxLevel) {
|
|
1583
|
+
const groupedByLevel = [];
|
|
1584
|
+
// For each level that should have subtotals
|
|
1585
|
+
for (let level = 0; level < maxLevel; level++) {
|
|
1586
|
+
const levelGroups = [];
|
|
1587
|
+
const groupMap = new Map();
|
|
1588
|
+
// Group columns by their dimension prefix at this level
|
|
1589
|
+
pivotColumns.forEach(column => {
|
|
1590
|
+
const columnKey = column.name.split('_')[0]; // Get part before aggregation
|
|
1591
|
+
const parts = columnKey.split('|');
|
|
1592
|
+
if (parts.length > level) {
|
|
1593
|
+
const prefix = parts.slice(0, level + 1);
|
|
1594
|
+
const prefixKey = prefix.join('|');
|
|
1595
|
+
if (!groupMap.has(prefixKey)) {
|
|
1596
|
+
groupMap.set(prefixKey, []);
|
|
1597
|
+
}
|
|
1598
|
+
groupMap.get(prefixKey).push(column);
|
|
1599
|
+
}
|
|
1600
|
+
});
|
|
1601
|
+
// Convert map to array format and sort
|
|
1602
|
+
Array.from(groupMap.keys()).sort().forEach(prefixKey => {
|
|
1603
|
+
const prefix = prefixKey.split('|');
|
|
1604
|
+
const columns = groupMap.get(prefixKey);
|
|
1605
|
+
levelGroups.push({ prefix, columns });
|
|
1606
|
+
});
|
|
1607
|
+
groupedByLevel.push(levelGroups);
|
|
1608
|
+
}
|
|
1609
|
+
return groupedByLevel;
|
|
1610
|
+
}
|
|
1611
|
+
/**
|
|
1612
|
+
* Create a subtotal column for a specific dimension prefix
|
|
1613
|
+
*/
|
|
1614
|
+
createSubtotalColumn(prefix, level, aggregation, gridConfiguration, gridIndex = 0) {
|
|
1615
|
+
const aggregationColumn = gridConfiguration?.fields.find((col) => col.name === aggregation.name);
|
|
1616
|
+
const subtotalColumnName = `_${prefix.join('_')}_subtotal_${aggregation.name}`;
|
|
1617
|
+
const subtotalLabel = level === 0
|
|
1618
|
+
? `${prefix[0]} Subtotal - ${aggregation.label || aggregation.name}`
|
|
1619
|
+
: `${prefix.join('+')} Subtotal - ${aggregation.label || aggregation.name}`;
|
|
1620
|
+
return {
|
|
1621
|
+
name: subtotalColumnName,
|
|
1622
|
+
label: subtotalLabel,
|
|
1623
|
+
datatype: this.getAggregationDataType(aggregation.aggregationFunction),
|
|
1624
|
+
field_size: aggregation.field_size || aggregationColumn?.field_size || 120,
|
|
1625
|
+
grid_index: gridIndex,
|
|
1626
|
+
isLeaf: true,
|
|
1627
|
+
level: level + 1,
|
|
1628
|
+
parentPath: prefix,
|
|
1629
|
+
rowspan: 1,
|
|
1630
|
+
colspan: 1,
|
|
1631
|
+
aggregationFunction: aggregation.aggregationFunction,
|
|
1632
|
+
enableDrilldown: aggregation.enableDrilldown || false
|
|
1633
|
+
};
|
|
1634
|
+
}
|
|
1302
1635
|
/**
|
|
1303
1636
|
* Add grand total row
|
|
1304
1637
|
*/
|
|
@@ -1738,126 +2071,294 @@ class PivotTransformService {
|
|
|
1738
2071
|
if (!enableColumnSubtotals || columnDimensions.length <= 1) {
|
|
1739
2072
|
return;
|
|
1740
2073
|
}
|
|
1741
|
-
//
|
|
1742
|
-
const
|
|
1743
|
-
// Extract unique values for each subtotal dimension dynamically
|
|
1744
|
-
const dimensionValues = subtotalDimensions.map((dimension) => {
|
|
2074
|
+
// Get all dimension values the same way as leaf columns
|
|
2075
|
+
const allDimensionValues = columnDimensions.map((dimension, dimIndex) => {
|
|
1745
2076
|
const values = new Set();
|
|
1746
2077
|
if (sourceData && sourceData.length > 0) {
|
|
1747
2078
|
sourceData.forEach(row => {
|
|
1748
|
-
const value = row[dimension]
|
|
1749
|
-
if (value) {
|
|
2079
|
+
const value = row[dimension]?.toString();
|
|
2080
|
+
if (value !== undefined && value !== null) {
|
|
1750
2081
|
values.add(value);
|
|
1751
2082
|
}
|
|
1752
2083
|
});
|
|
1753
2084
|
}
|
|
2085
|
+
else {
|
|
2086
|
+
// No fallback - if no source data, return empty values
|
|
2087
|
+
// Generic pivot grid should not have hardcoded dimension values
|
|
2088
|
+
}
|
|
1754
2089
|
return Array.from(values).sort();
|
|
1755
2090
|
});
|
|
1756
|
-
//
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
2091
|
+
// Add subtotal headers at correct row levels, then rearrange to match leaf column order
|
|
2092
|
+
this.addSubtotalHeadersAtCorrectLevels(headerRows, allDimensionValues, aggregations, columnDimensions);
|
|
2093
|
+
this.rearrangeHeadersToMatchLeafColumns(headerRows, columnDimensions, subtotalPositionColumn, sourceData || []);
|
|
2094
|
+
}
|
|
2095
|
+
/**
|
|
2096
|
+
* Add subtotal headers at their correct row levels
|
|
2097
|
+
*/
|
|
2098
|
+
addSubtotalHeadersAtCorrectLevels(headerRows, dimensionValues, aggregations, columnDimensions) {
|
|
2099
|
+
// Generate hierarchical subtotal combinations
|
|
2100
|
+
const subtotalCombinations = this.generateSubtotalCombinations(dimensionValues, columnDimensions.length - 1);
|
|
2101
|
+
subtotalCombinations.forEach(combination => {
|
|
2102
|
+
const { dimensionPath, level } = combination;
|
|
2103
|
+
aggregations.forEach(aggregation => {
|
|
2104
|
+
if (aggregation.showSubtotals) {
|
|
2105
|
+
const subtotalHeader = {
|
|
2106
|
+
name: `_${dimensionPath.join('_')}_subtotal_${aggregation.name}`,
|
|
2107
|
+
label: level === 0
|
|
2108
|
+
? `${dimensionPath[0]} Subtotal - ${aggregation.label || aggregation.name}`
|
|
2109
|
+
: `${dimensionPath.join('+')} Subtotal - ${aggregation.label || aggregation.name}`,
|
|
2110
|
+
level: level + 1, // Place subtotal at the next level down from its dimension
|
|
2111
|
+
colspan: 1,
|
|
2112
|
+
rowspan: headerRows.length - (level + 1), // Span remaining rows
|
|
2113
|
+
parentGroupKey: level === 0 ? dimensionPath[0] : dimensionPath.join('+'), // Parent group identifier for target row
|
|
2114
|
+
parentValue: level === 0 ? dimensionPath[0] : dimensionPath.join('+'), // Parent group value for easier identification
|
|
2115
|
+
isSubtotal: true
|
|
2116
|
+
};
|
|
2117
|
+
// Add to the appropriate row based on dimension level
|
|
2118
|
+
// Priority subtotals go to Rating row (level 1), Rating subtotals go to Status row (level 2)
|
|
2119
|
+
const targetRow = Math.min(level + 1, headerRows.length - 1);
|
|
2120
|
+
headerRows[targetRow].push(subtotalHeader);
|
|
2121
|
+
}
|
|
2122
|
+
});
|
|
2123
|
+
});
|
|
2124
|
+
}
|
|
2125
|
+
/**
|
|
2126
|
+
* Rearrange headers in each row to match the leaf column order
|
|
2127
|
+
*/
|
|
2128
|
+
rearrangeHeadersToMatchLeafColumns(headerRows, columnDimensions, subtotalPositionColumn, sourceData) {
|
|
2129
|
+
// Process each header row to reorder headers to match leaf column structure
|
|
2130
|
+
headerRows.forEach((headerRow, rowIndex) => {
|
|
2131
|
+
if (rowIndex >= columnDimensions.length)
|
|
2132
|
+
return; // Skip aggregation level
|
|
2133
|
+
// Separate pivot headers from row dimension headers
|
|
2134
|
+
const rowDimensionHeaders = headerRow.filter(h => !columnDimensions.includes(h.name) && !h.name.includes('|') && !h.name.includes('_subtotal_'));
|
|
2135
|
+
const pivotHeaders = headerRow.filter(h => columnDimensions.includes(h.name) || h.name.includes('|'));
|
|
2136
|
+
const subtotalHeaders = headerRow.filter(h => h.name.includes('_subtotal_'));
|
|
2137
|
+
// Group headers by their hierarchical position to match leaf column order
|
|
2138
|
+
const reorderedHeaders = this.reorderHeadersByHierarchy(pivotHeaders, subtotalHeaders, rowIndex, columnDimensions, subtotalPositionColumn, sourceData || []);
|
|
2139
|
+
// Clear and rebuild the header row
|
|
2140
|
+
headerRow.length = 0;
|
|
2141
|
+
headerRow.push(...rowDimensionHeaders, ...reorderedHeaders);
|
|
2142
|
+
});
|
|
2143
|
+
}
|
|
2144
|
+
/**
|
|
2145
|
+
* Reorder headers by hierarchy to match leaf column interleaved positioning - GENERIC VERSION
|
|
2146
|
+
*/
|
|
2147
|
+
reorderHeadersByHierarchy(pivotHeaders, subtotalHeaders, rowIndex, columnDimensions, subtotalPositionColumn, sourceData) {
|
|
2148
|
+
const reordered = [];
|
|
2149
|
+
if (rowIndex === 0) {
|
|
2150
|
+
// Priority level - just add priority headers as they are
|
|
2151
|
+
reordered.push(...pivotHeaders);
|
|
2152
|
+
}
|
|
2153
|
+
else if (rowIndex === 1) {
|
|
2154
|
+
// Rating level - interleave rating headers with priority subtotals
|
|
2155
|
+
// Add parentGroupKey to regular headers based on their position
|
|
2156
|
+
this.addParentGroupKeysToHeaders(pivotHeaders, rowIndex, sourceData, columnDimensions);
|
|
2157
|
+
// Group rating headers by their priority parent using parentGroupKey
|
|
2158
|
+
const ratingGroups = this.groupHeadersByParentKey(pivotHeaders);
|
|
2159
|
+
const prioritySubtotals = this.groupSubtotalsByParentKey(subtotalHeaders);
|
|
2160
|
+
// Process each first dimension group in correct order (dynamically extracted)
|
|
2161
|
+
const firstDimensionValues = this.extractDimensionValues(sourceData, columnDimensions[0]);
|
|
2162
|
+
firstDimensionValues.forEach(firstDimensionValue => {
|
|
2163
|
+
const ratingHeaders = ratingGroups[firstDimensionValue] || [];
|
|
2164
|
+
const subtotals = prioritySubtotals[firstDimensionValue] || [];
|
|
2165
|
+
// Add rating headers for this priority
|
|
2166
|
+
if (subtotalPositionColumn === 'before') {
|
|
2167
|
+
// Add subtotals first, then rating headers
|
|
2168
|
+
reordered.push(...subtotals);
|
|
2169
|
+
reordered.push(...ratingHeaders);
|
|
2170
|
+
}
|
|
2171
|
+
else {
|
|
2172
|
+
// Add rating headers first, then subtotals
|
|
2173
|
+
reordered.push(...ratingHeaders);
|
|
2174
|
+
reordered.push(...subtotals);
|
|
2175
|
+
}
|
|
2176
|
+
});
|
|
2177
|
+
}
|
|
2178
|
+
else if (rowIndex === 2) {
|
|
2179
|
+
// Status level - interleave status headers with rating subtotals
|
|
2180
|
+
this.addParentGroupKeysToHeaders(pivotHeaders, rowIndex, sourceData, columnDimensions);
|
|
2181
|
+
const statusGroups = this.groupHeadersByParentKey(pivotHeaders);
|
|
2182
|
+
const ratingSubtotals = this.groupSubtotalsByParentKey(subtotalHeaders);
|
|
2183
|
+
console.log(`DEBUG: Status groups with parentKey: ${Object.keys(statusGroups).length}, Rating subtotals: ${Object.keys(ratingSubtotals).length}`);
|
|
2184
|
+
// Process each dimension combination dynamically
|
|
2185
|
+
const firstDimensionValues = this.extractDimensionValues(sourceData, columnDimensions[0]);
|
|
2186
|
+
const secondDimensionValues = this.extractDimensionValues(sourceData, columnDimensions[1]);
|
|
2187
|
+
firstDimensionValues.forEach(firstDimensionValue => {
|
|
2188
|
+
secondDimensionValues.forEach(secondDimensionValue => {
|
|
2189
|
+
const groupKey = `${firstDimensionValue}+${secondDimensionValue}`;
|
|
2190
|
+
const statusHeaders = statusGroups[groupKey] || [];
|
|
2191
|
+
const subtotals = ratingSubtotals[groupKey] || [];
|
|
2192
|
+
if (subtotalPositionColumn === 'before') {
|
|
2193
|
+
// Add subtotals first, then status headers
|
|
2194
|
+
reordered.push(...subtotals);
|
|
2195
|
+
reordered.push(...statusHeaders);
|
|
2196
|
+
}
|
|
2197
|
+
else {
|
|
2198
|
+
// Add status headers first, then subtotals
|
|
2199
|
+
reordered.push(...statusHeaders);
|
|
2200
|
+
reordered.push(...subtotals);
|
|
1785
2201
|
}
|
|
1786
2202
|
});
|
|
1787
2203
|
});
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
2204
|
+
}
|
|
2205
|
+
return reordered;
|
|
2206
|
+
}
|
|
2207
|
+
/**
|
|
2208
|
+
* Extract unique values for a specific column dimension from source data
|
|
2209
|
+
*/
|
|
2210
|
+
extractDimensionValues(sourceData, dimension) {
|
|
2211
|
+
const values = new Set();
|
|
2212
|
+
sourceData.forEach(row => {
|
|
2213
|
+
const value = row[dimension] != null ? String(row[dimension]) : '';
|
|
2214
|
+
values.add(String(value));
|
|
2215
|
+
});
|
|
2216
|
+
return Array.from(values).sort();
|
|
2217
|
+
}
|
|
2218
|
+
/**
|
|
2219
|
+
* Extract all dimension values arrays from headers or source data
|
|
2220
|
+
*/
|
|
2221
|
+
extractAllDimensionValues(sourceData, columnDimensions) {
|
|
2222
|
+
return columnDimensions.map(dimension => this.extractDimensionValues(sourceData, dimension));
|
|
2223
|
+
}
|
|
2224
|
+
/**
|
|
2225
|
+
* Group rating headers by their priority parent - GENERIC VERSION
|
|
2226
|
+
*/
|
|
2227
|
+
groupRatingHeadersByPriority(headers, sourceData, columnDimensions) {
|
|
2228
|
+
const groups = {};
|
|
2229
|
+
if (columnDimensions.length >= 2) {
|
|
2230
|
+
const firstDimensionValues = this.extractDimensionValues(sourceData, columnDimensions[0]);
|
|
2231
|
+
const secondDimensionCount = this.extractDimensionValues(sourceData, columnDimensions[1]).length;
|
|
2232
|
+
headers.forEach(header => {
|
|
2233
|
+
const headerIndex = headers.indexOf(header);
|
|
2234
|
+
const firstDimensionIndex = Math.floor(headerIndex / secondDimensionCount);
|
|
2235
|
+
const firstDimensionValue = firstDimensionValues[firstDimensionIndex] || firstDimensionValues[0] || '';
|
|
2236
|
+
if (!groups[firstDimensionValue])
|
|
2237
|
+
groups[firstDimensionValue] = [];
|
|
2238
|
+
groups[firstDimensionValue].push(header);
|
|
1793
2239
|
});
|
|
1794
2240
|
}
|
|
2241
|
+
return groups;
|
|
1795
2242
|
}
|
|
1796
2243
|
/**
|
|
1797
|
-
*
|
|
2244
|
+
* Group subtotals by their priority
|
|
1798
2245
|
*/
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
// Find the range of headers that belong to this dimension value
|
|
1808
|
-
// Look for headers with parentValue matching this dimension value
|
|
1809
|
-
let startIndex = -1;
|
|
1810
|
-
let endIndex = -1;
|
|
1811
|
-
for (let i = 0; i < headerRow.length; i++) {
|
|
1812
|
-
const header = headerRow[i];
|
|
1813
|
-
// Check if this header belongs to our dimension group
|
|
1814
|
-
// For level 1 subtotals, we look for headers that have this dimension as their parent
|
|
1815
|
-
if (header.parentValue === dimensionValue) {
|
|
1816
|
-
if (startIndex === -1)
|
|
1817
|
-
startIndex = i;
|
|
1818
|
-
endIndex = i;
|
|
1819
|
-
}
|
|
2246
|
+
groupSubtotalsByPriority(subtotals) {
|
|
2247
|
+
const groups = {};
|
|
2248
|
+
subtotals.forEach(subtotal => {
|
|
2249
|
+
if (subtotal.label && subtotal.label.includes('Subtotal')) {
|
|
2250
|
+
const priority = subtotal.label.split(' ')[0]; // Extract "Low", "Medium", "Urgent"
|
|
2251
|
+
if (!groups[priority])
|
|
2252
|
+
groups[priority] = [];
|
|
2253
|
+
groups[priority].push(subtotal);
|
|
1820
2254
|
}
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
2255
|
+
});
|
|
2256
|
+
return groups;
|
|
2257
|
+
}
|
|
2258
|
+
/**
|
|
2259
|
+
* Group status headers by their rating parent (combination) - GENERIC VERSION
|
|
2260
|
+
*/
|
|
2261
|
+
groupStatusHeadersByRating(headers, sourceData, columnDimensions) {
|
|
2262
|
+
const groups = {};
|
|
2263
|
+
if (columnDimensions.length >= 3) {
|
|
2264
|
+
const firstDimensionValues = this.extractDimensionValues(sourceData, columnDimensions[0]);
|
|
2265
|
+
const secondDimensionValues = this.extractDimensionValues(sourceData, columnDimensions[1]);
|
|
2266
|
+
const thirdDimensionCount = this.extractDimensionValues(sourceData, columnDimensions[2]).length;
|
|
2267
|
+
headers.forEach(header => {
|
|
2268
|
+
const headerIndex = headers.indexOf(header);
|
|
2269
|
+
const secondDimensionIndex = Math.floor(headerIndex / thirdDimensionCount);
|
|
2270
|
+
const firstDimensionIndex = Math.floor(secondDimensionIndex / secondDimensionValues.length);
|
|
2271
|
+
const firstDimensionValue = firstDimensionValues[firstDimensionIndex] || firstDimensionValues[0] || '';
|
|
2272
|
+
const secondDimensionValue = secondDimensionValues[secondDimensionIndex % secondDimensionValues.length] || secondDimensionValues[0] || '';
|
|
2273
|
+
const groupKey = `${firstDimensionValue}+${secondDimensionValue}`;
|
|
2274
|
+
if (!groups[groupKey])
|
|
2275
|
+
groups[groupKey] = [];
|
|
2276
|
+
groups[groupKey].push(header);
|
|
2277
|
+
});
|
|
2278
|
+
}
|
|
2279
|
+
return groups;
|
|
2280
|
+
}
|
|
2281
|
+
/**
|
|
2282
|
+
* Group subtotals by their rating (Priority+Rating combination)
|
|
2283
|
+
*/
|
|
2284
|
+
groupSubtotalsByRating(subtotals) {
|
|
2285
|
+
const groups = {};
|
|
2286
|
+
subtotals.forEach(subtotal => {
|
|
2287
|
+
if (subtotal.label && subtotal.label.includes('+') && subtotal.label.includes('Subtotal')) {
|
|
2288
|
+
const parts = subtotal.label.split(' ')[0]; // Extract "Low+0", "Medium+1", etc.
|
|
2289
|
+
if (!groups[parts])
|
|
2290
|
+
groups[parts] = [];
|
|
2291
|
+
groups[parts].push(subtotal);
|
|
1829
2292
|
}
|
|
1830
|
-
|
|
1831
|
-
|
|
2293
|
+
});
|
|
2294
|
+
return groups;
|
|
2295
|
+
}
|
|
2296
|
+
/**
|
|
2297
|
+
* Add parent group keys to headers based on their position and row level - GENERIC VERSION
|
|
2298
|
+
*/
|
|
2299
|
+
addParentGroupKeysToHeaders(headers, rowIndex, sourceData, columnDimensions) {
|
|
2300
|
+
if (!columnDimensions || columnDimensions.length === 0 || rowIndex < 1) {
|
|
2301
|
+
return;
|
|
1832
2302
|
}
|
|
1833
|
-
//
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
//
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
matchEnd = i;
|
|
1847
|
-
}
|
|
2303
|
+
// Extract all dimension values dynamically
|
|
2304
|
+
const dimensionValues = this.extractAllDimensionValues(sourceData, columnDimensions);
|
|
2305
|
+
if (rowIndex === 1) {
|
|
2306
|
+
// Second level (e.g., Rating level) - assign parent first dimension group keys
|
|
2307
|
+
const firstDimensionValues = dimensionValues[0] || [];
|
|
2308
|
+
if (dimensionValues.length >= 2) {
|
|
2309
|
+
const secondDimensionCount = dimensionValues[1].length;
|
|
2310
|
+
headers.forEach((header, index) => {
|
|
2311
|
+
const firstDimensionIndex = Math.floor(index / secondDimensionCount);
|
|
2312
|
+
const firstDimensionValue = firstDimensionValues[firstDimensionIndex] || firstDimensionValues[0] || '';
|
|
2313
|
+
header.parentGroupKey = firstDimensionValue;
|
|
2314
|
+
header.parentValue = firstDimensionValue;
|
|
2315
|
+
});
|
|
1848
2316
|
}
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
2317
|
+
}
|
|
2318
|
+
else if (rowIndex === 2) {
|
|
2319
|
+
// Third level (e.g., Status level) - assign parent combination group keys
|
|
2320
|
+
if (dimensionValues.length >= 3) {
|
|
2321
|
+
const firstDimensionValues = dimensionValues[0] || [];
|
|
2322
|
+
const secondDimensionValues = dimensionValues[1] || [];
|
|
2323
|
+
const thirdDimensionCount = dimensionValues[2].length;
|
|
2324
|
+
headers.forEach((header, index) => {
|
|
2325
|
+
const secondDimensionIndex = Math.floor(index / thirdDimensionCount);
|
|
2326
|
+
const firstDimensionIndex = Math.floor(secondDimensionIndex / secondDimensionValues.length);
|
|
2327
|
+
const firstDimensionValue = firstDimensionValues[firstDimensionIndex] || firstDimensionValues[0] || '';
|
|
2328
|
+
const secondDimensionValue = secondDimensionValues[secondDimensionIndex % secondDimensionValues.length] || secondDimensionValues[0] || '';
|
|
2329
|
+
const parentKey = `${firstDimensionValue}+${secondDimensionValue}`;
|
|
2330
|
+
header.parentGroupKey = parentKey;
|
|
2331
|
+
header.parentValue = parentKey;
|
|
2332
|
+
});
|
|
1856
2333
|
}
|
|
1857
|
-
// Fallback: append at end
|
|
1858
|
-
return headerRow.length;
|
|
1859
2334
|
}
|
|
1860
2335
|
}
|
|
2336
|
+
/**
|
|
2337
|
+
* Group headers by their parentGroupKey
|
|
2338
|
+
*/
|
|
2339
|
+
groupHeadersByParentKey(headers) {
|
|
2340
|
+
const groups = {};
|
|
2341
|
+
headers.forEach(header => {
|
|
2342
|
+
const parentKey = header.parentGroupKey || 'Unknown';
|
|
2343
|
+
if (!groups[parentKey])
|
|
2344
|
+
groups[parentKey] = [];
|
|
2345
|
+
groups[parentKey].push(header);
|
|
2346
|
+
});
|
|
2347
|
+
return groups;
|
|
2348
|
+
}
|
|
2349
|
+
/**
|
|
2350
|
+
* Group subtotals by their parentGroupKey
|
|
2351
|
+
*/
|
|
2352
|
+
groupSubtotalsByParentKey(subtotals) {
|
|
2353
|
+
const groups = {};
|
|
2354
|
+
subtotals.forEach(subtotal => {
|
|
2355
|
+
const parentKey = subtotal.parentGroupKey || 'Unknown';
|
|
2356
|
+
if (!groups[parentKey])
|
|
2357
|
+
groups[parentKey] = [];
|
|
2358
|
+
groups[parentKey].push(subtotal);
|
|
2359
|
+
});
|
|
2360
|
+
return groups;
|
|
2361
|
+
}
|
|
1861
2362
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: PivotTransformService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1862
2363
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: PivotTransformService, providedIn: 'root' });
|
|
1863
2364
|
}
|
|
@@ -2244,6 +2745,7 @@ class EruGridStore {
|
|
|
2244
2745
|
this._configuration.set(this.validateAndEnforceConfiguration(mergedConfig));
|
|
2245
2746
|
}
|
|
2246
2747
|
setPivotConfiguration(pivotConfig) {
|
|
2748
|
+
console.log('🔄 SET PIVOT CONFIGURATION: Setting pivot configuration', pivotConfig);
|
|
2247
2749
|
this._pivotConfiguration.set(pivotConfig);
|
|
2248
2750
|
// Also store in the main configuration for consistency
|
|
2249
2751
|
this.updateGridConfiguration({ pivot: pivotConfig });
|
|
@@ -2406,13 +2908,10 @@ class EruGridStore {
|
|
|
2406
2908
|
this._pivotDrilldown.set(null);
|
|
2407
2909
|
}
|
|
2408
2910
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: EruGridStore, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
2409
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: EruGridStore
|
|
2911
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: EruGridStore });
|
|
2410
2912
|
}
|
|
2411
2913
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: EruGridStore, decorators: [{
|
|
2412
|
-
type: Injectable
|
|
2413
|
-
args: [{
|
|
2414
|
-
providedIn: 'root'
|
|
2415
|
-
}]
|
|
2914
|
+
type: Injectable
|
|
2416
2915
|
}] });
|
|
2417
2916
|
|
|
2418
2917
|
class ThemeService {
|
|
@@ -2767,13 +3266,10 @@ class EruGridService {
|
|
|
2767
3266
|
return this.eruGridStore;
|
|
2768
3267
|
}
|
|
2769
3268
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: EruGridService, deps: [{ token: EruGridStore }, { token: ThemeService }, { token: ColumnConstraintsService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
2770
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: EruGridService
|
|
3269
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: EruGridService });
|
|
2771
3270
|
}
|
|
2772
3271
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: EruGridService, decorators: [{
|
|
2773
|
-
type: Injectable
|
|
2774
|
-
args: [{
|
|
2775
|
-
providedIn: 'root'
|
|
2776
|
-
}]
|
|
3272
|
+
type: Injectable
|
|
2777
3273
|
}], ctorParameters: () => [{ type: EruGridStore }, { type: ThemeService }, { type: ColumnConstraintsService }] });
|
|
2778
3274
|
|
|
2779
3275
|
class CustomDateAdapter extends NativeDateAdapter {
|
|
@@ -3167,7 +3663,7 @@ class DataCellComponent {
|
|
|
3167
3663
|
peopleTrigger;
|
|
3168
3664
|
elementRef = inject(ElementRef);
|
|
3169
3665
|
sanitize = inject(DomSanitizer);
|
|
3170
|
-
eruGridStore =
|
|
3666
|
+
eruGridStore = input.required(...(ngDevMode ? [{ debugName: "eruGridStore" }] : []));
|
|
3171
3667
|
overlayMenuService = inject(OverlayMenuService);
|
|
3172
3668
|
el = inject(ElementRef);
|
|
3173
3669
|
cdr = inject(ChangeDetectorRef);
|
|
@@ -3216,7 +3712,7 @@ class DataCellComponent {
|
|
|
3216
3712
|
}
|
|
3217
3713
|
// Computed property to get current column width that updates on resize
|
|
3218
3714
|
currentColumnWidth = computed(() => {
|
|
3219
|
-
const columns = this.eruGridStore.columns();
|
|
3715
|
+
const columns = this.eruGridStore().columns();
|
|
3220
3716
|
const currentColumn = columns?.find(col => col.name === this.columnName());
|
|
3221
3717
|
return currentColumn?.field_size || this.fieldSize();
|
|
3222
3718
|
}, ...(ngDevMode ? [{ debugName: "currentColumnWidth" }] : []));
|
|
@@ -3240,7 +3736,7 @@ class DataCellComponent {
|
|
|
3240
3736
|
}
|
|
3241
3737
|
;
|
|
3242
3738
|
columnCellConfiguration = computed(() => {
|
|
3243
|
-
const columns = this.eruGridStore.columns();
|
|
3739
|
+
const columns = this.eruGridStore().columns();
|
|
3244
3740
|
return columns?.find((columnConfig) => columnConfig.name === this.getColumnValue().replace('default_', '')) || null;
|
|
3245
3741
|
}, ...(ngDevMode ? [{ debugName: "columnCellConfiguration" }] : []));
|
|
3246
3742
|
renderer = inject(Renderer2);
|
|
@@ -3255,7 +3751,7 @@ class DataCellComponent {
|
|
|
3255
3751
|
const result = validator(value, config);
|
|
3256
3752
|
this.error.set(result.error || '');
|
|
3257
3753
|
if (result.isValid) {
|
|
3258
|
-
this.eruGridStore.setActiveCell(null);
|
|
3754
|
+
this.eruGridStore().setActiveCell(null);
|
|
3259
3755
|
}
|
|
3260
3756
|
}
|
|
3261
3757
|
}
|
|
@@ -3345,14 +3841,14 @@ class DataCellComponent {
|
|
|
3345
3841
|
this.setColumnTextAlign();
|
|
3346
3842
|
// currentValue is now automatically synchronized via effect in constructor
|
|
3347
3843
|
}
|
|
3348
|
-
isActive = computed(() => this.eruGridStore.activeCell() === this.el.nativeElement, ...(ngDevMode ? [{ debugName: "isActive" }] : []));
|
|
3844
|
+
isActive = computed(() => this.eruGridStore().activeCell() === this.el.nativeElement, ...(ngDevMode ? [{ debugName: "isActive" }] : []));
|
|
3349
3845
|
setActive(active) {
|
|
3350
3846
|
if (active && this.isEditable() && this.mode() === 'table') {
|
|
3351
|
-
this.eruGridStore.setActiveCell(this.el.nativeElement);
|
|
3847
|
+
this.eruGridStore().setActiveCell(this.el.nativeElement);
|
|
3352
3848
|
this.focusAndPositionCursor();
|
|
3353
3849
|
}
|
|
3354
3850
|
else {
|
|
3355
|
-
this.eruGridStore.setActiveCell(null);
|
|
3851
|
+
this.eruGridStore().setActiveCell(null);
|
|
3356
3852
|
}
|
|
3357
3853
|
}
|
|
3358
3854
|
/* onDoubleClick(): void {
|
|
@@ -3375,7 +3871,7 @@ class DataCellComponent {
|
|
|
3375
3871
|
const columnValues = this.columnName().includes('_') ? this.columnName().split(/_(.*)/)[0] : '';
|
|
3376
3872
|
const tmpStr = idSplit[0].replace('pivot_', '');
|
|
3377
3873
|
const rowId = tmpStr.includes('_') ? tmpStr.split(/_(.*)/)[0] : tmpStr;
|
|
3378
|
-
this.eruGridStore.setPivotDrilldown({
|
|
3874
|
+
this.eruGridStore().setPivotDrilldown({
|
|
3379
3875
|
frozenGrandTotalCell: this.frozenGrandTotalCell(),
|
|
3380
3876
|
columnName: columnName,
|
|
3381
3877
|
rowId: rowId,
|
|
@@ -3441,7 +3937,7 @@ class DataCellComponent {
|
|
|
3441
3937
|
ngOnInit() {
|
|
3442
3938
|
// Add document click listener to close attachment menu
|
|
3443
3939
|
document.addEventListener('click', this.onDocumentClick.bind(this));
|
|
3444
|
-
this.replaceZeroValue = this.eruGridStore.configuration().config?.replaceZeroValue;
|
|
3940
|
+
this.replaceZeroValue = this.eruGridStore().configuration().config?.replaceZeroValue;
|
|
3445
3941
|
}
|
|
3446
3942
|
ngOnDestroy() {
|
|
3447
3943
|
// Remove document click listener
|
|
@@ -3477,7 +3973,7 @@ class DataCellComponent {
|
|
|
3477
3973
|
break;
|
|
3478
3974
|
default:
|
|
3479
3975
|
// For other cell types, just deactivate
|
|
3480
|
-
this.eruGridStore.setActiveCell(null);
|
|
3976
|
+
this.eruGridStore().setActiveCell(null);
|
|
3481
3977
|
break;
|
|
3482
3978
|
}
|
|
3483
3979
|
}
|
|
@@ -3505,7 +4001,7 @@ class DataCellComponent {
|
|
|
3505
4001
|
}
|
|
3506
4002
|
else {
|
|
3507
4003
|
this.error.set(``);
|
|
3508
|
-
this.eruGridStore.setActiveCell(null);
|
|
4004
|
+
this.eruGridStore().setActiveCell(null);
|
|
3509
4005
|
}
|
|
3510
4006
|
}
|
|
3511
4007
|
openDatePicker() {
|
|
@@ -4002,7 +4498,7 @@ class DataCellComponent {
|
|
|
4002
4498
|
return this.getColumnValue().replace('default_', '');
|
|
4003
4499
|
}
|
|
4004
4500
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: DataCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4005
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.3", type: DataCellComponent, isStandalone: true, selector: "data-cell", inputs: { fieldSize: { classPropertyName: "fieldSize", publicName: "fieldSize", isSignal: true, isRequired: true, transformFunction: null }, columnDatatype: { classPropertyName: "columnDatatype", publicName: "columnDatatype", isSignal: true, isRequired: true, transformFunction: null }, columnName: { classPropertyName: "columnName", publicName: "columnName", isSignal: true, isRequired: true, transformFunction: null }, column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: true, transformFunction: null }, frozenGrandTotalCell: { classPropertyName: "frozenGrandTotalCell", publicName: "frozenGrandTotalCell", isSignal: true, isRequired: false, transformFunction: null }, td: { classPropertyName: "td", publicName: "td", isSignal: true, isRequired: false, transformFunction: null }, drillable: { classPropertyName: "drillable", publicName: "drillable", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, isEditable: { classPropertyName: "isEditable", publicName: "isEditable", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { td: "tdChange" }, host: { listeners: { "document:click": "onDocumentClick($event)" }, classAttribute: "data-cell-component" }, providers: [MatDatepickerModule,
|
|
4501
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.3", type: DataCellComponent, isStandalone: true, selector: "data-cell", inputs: { eruGridStore: { classPropertyName: "eruGridStore", publicName: "eruGridStore", isSignal: true, isRequired: true, transformFunction: null }, fieldSize: { classPropertyName: "fieldSize", publicName: "fieldSize", isSignal: true, isRequired: true, transformFunction: null }, columnDatatype: { classPropertyName: "columnDatatype", publicName: "columnDatatype", isSignal: true, isRequired: true, transformFunction: null }, columnName: { classPropertyName: "columnName", publicName: "columnName", isSignal: true, isRequired: true, transformFunction: null }, column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: true, transformFunction: null }, frozenGrandTotalCell: { classPropertyName: "frozenGrandTotalCell", publicName: "frozenGrandTotalCell", isSignal: true, isRequired: false, transformFunction: null }, td: { classPropertyName: "td", publicName: "td", isSignal: true, isRequired: false, transformFunction: null }, drillable: { classPropertyName: "drillable", publicName: "drillable", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, isEditable: { classPropertyName: "isEditable", publicName: "isEditable", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { td: "tdChange" }, host: { listeners: { "document:click": "onDocumentClick($event)" }, classAttribute: "data-cell-component" }, providers: [MatDatepickerModule,
|
|
4006
4502
|
MatNativeDateModule,
|
|
4007
4503
|
DatePipe,
|
|
4008
4504
|
{
|
|
@@ -4316,6 +4812,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImpor
|
|
|
4316
4812
|
|
|
4317
4813
|
class EruGridComponent {
|
|
4318
4814
|
changeDetectorRef;
|
|
4815
|
+
gridService = inject(EruGridService);
|
|
4319
4816
|
gridStore = inject(EruGridStore);
|
|
4320
4817
|
elementRef = inject(ElementRef);
|
|
4321
4818
|
resizeObserver = null;
|
|
@@ -4325,6 +4822,7 @@ class EruGridComponent {
|
|
|
4325
4822
|
rowContainer;
|
|
4326
4823
|
headerScroller;
|
|
4327
4824
|
gtScroller;
|
|
4825
|
+
gridConfig;
|
|
4328
4826
|
initialMinHeight = 400;
|
|
4329
4827
|
initialTotalWidth = 0;
|
|
4330
4828
|
vp;
|
|
@@ -4447,7 +4945,6 @@ class EruGridComponent {
|
|
|
4447
4945
|
grandTotalHeight = 60;
|
|
4448
4946
|
}
|
|
4449
4947
|
let minHeight = (this.gridHeight() - headerHeight); // Start with 50% of parent
|
|
4450
|
-
console.log(minHeight, rowHeight, headerHeight, this.gridHeight());
|
|
4451
4948
|
// Ensure minimum usable height
|
|
4452
4949
|
minHeight = Math.min(minHeight, rowHeight);
|
|
4453
4950
|
minHeight = minHeight + 1;
|
|
@@ -4510,6 +5007,12 @@ class EruGridComponent {
|
|
|
4510
5007
|
}
|
|
4511
5008
|
}
|
|
4512
5009
|
ngOnInit() {
|
|
5010
|
+
if (this.gridConfig) {
|
|
5011
|
+
this.gridService.set_table_configuration(this.gridConfig);
|
|
5012
|
+
}
|
|
5013
|
+
if (this.gridConfig?.mode === 'pivot') {
|
|
5014
|
+
this.gridService.set_grid_mode(this.gridConfig.mode);
|
|
5015
|
+
}
|
|
4513
5016
|
this.initializeColumnWidths();
|
|
4514
5017
|
this.initializeGroups();
|
|
4515
5018
|
this.firstDataRowIndex.set(this.maxDepth() + 1);
|
|
@@ -5231,18 +5734,18 @@ class EruGridComponent {
|
|
|
5231
5734
|
}, 100);
|
|
5232
5735
|
}
|
|
5233
5736
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: EruGridComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
5234
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.3", type: EruGridComponent, isStandalone: true, selector: "eru-grid", 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 </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]=\"50\"\n class=\"viewport table-viewport\"\n (scrolledIndexChange)=\"onScroll($event)\"\n >\n <div class=\"table-wrapper\">\n <table class=\"eru-grid-table\" [class.show-column-lines]=\"showColumnLines()\" [class.show-column-lines]=\"showColumnLines()\">\n <thead>\n <tr style=\"visibility:hidden;\">\n <th class=\"checkbox-column\"></th>\n <th *ngFor=\"let column of columns(); trackBy: trackByColumnFn\"\n [style.width.px]=\"column.field_size\"\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'\"\n class=\"group-header\"\n (click)=\"toggleGroupExpansion(groupItem.group?.id || '')\"\n >\n <td class=\"checkbox-column\" style=\"border: none;\">\n {{ groupItem.group?.isExpanded ? '\u25BC' : '\u25B6' }}\n </td>\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'\" style=\"background:#fafafa\">\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 <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 <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 <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 ></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]=\"columns().length + 1\" 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 <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 </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}.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: ["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 });
|
|
5737
|
+
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]=\"50\"\n class=\"viewport table-viewport\"\n (scrolledIndexChange)=\"onScroll($event)\"\n >\n <div class=\"table-wrapper\">\n <table class=\"eru-grid-table\" [class.show-column-lines]=\"showColumnLines()\" [class.show-column-lines]=\"showColumnLines()\">\n <thead>\n <tr style=\"visibility:hidden;\">\n <th class=\"checkbox-column\"></th>\n <th *ngFor=\"let column of columns(); trackBy: trackByColumnFn\"\n [style.width.px]=\"column.field_size\"\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'\"\n class=\"group-header\"\n (click)=\"toggleGroupExpansion(groupItem.group?.id || '')\"\n >\n <td class=\"checkbox-column\" style=\"border: none;\">\n {{ groupItem.group?.isExpanded ? '\u25BC' : '\u25B6' }}\n </td>\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'\" style=\"background:#fafafa\">\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 <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 <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 <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]=\"columns().length + 1\" 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 <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}.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 });
|
|
5235
5738
|
}
|
|
5236
5739
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: EruGridComponent, decorators: [{
|
|
5237
5740
|
type: Component,
|
|
5238
|
-
args: [{ selector: 'eru-grid', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, standalone: true, imports: [
|
|
5741
|
+
args: [{ selector: 'eru-grid', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, standalone: true, providers: [EruGridStore, EruGridService], imports: [
|
|
5239
5742
|
DataCellComponent,
|
|
5240
5743
|
ScrollingModule,
|
|
5241
5744
|
CommonModule,
|
|
5242
5745
|
MatTooltipModule,
|
|
5243
5746
|
ResizeColumnDirective,
|
|
5244
5747
|
ColumnDragDirective
|
|
5245
|
-
], 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 </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]=\"50\"\n class=\"viewport table-viewport\"\n (scrolledIndexChange)=\"onScroll($event)\"\n >\n <div class=\"table-wrapper\">\n <table class=\"eru-grid-table\" [class.show-column-lines]=\"showColumnLines()\" [class.show-column-lines]=\"showColumnLines()\">\n <thead>\n <tr style=\"visibility:hidden;\">\n <th class=\"checkbox-column\"></th>\n <th *ngFor=\"let column of columns(); trackBy: trackByColumnFn\"\n [style.width.px]=\"column.field_size\"\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'\"\n class=\"group-header\"\n (click)=\"toggleGroupExpansion(groupItem.group?.id || '')\"\n >\n <td class=\"checkbox-column\" style=\"border: none;\">\n {{ groupItem.group?.isExpanded ? '\u25BC' : '\u25B6' }}\n </td>\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'\" style=\"background:#fafafa\">\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 <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 <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 <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 ></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]=\"columns().length + 1\" 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 <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 </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}.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"] }]
|
|
5748
|
+
], 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]=\"50\"\n class=\"viewport table-viewport\"\n (scrolledIndexChange)=\"onScroll($event)\"\n >\n <div class=\"table-wrapper\">\n <table class=\"eru-grid-table\" [class.show-column-lines]=\"showColumnLines()\" [class.show-column-lines]=\"showColumnLines()\">\n <thead>\n <tr style=\"visibility:hidden;\">\n <th class=\"checkbox-column\"></th>\n <th *ngFor=\"let column of columns(); trackBy: trackByColumnFn\"\n [style.width.px]=\"column.field_size\"\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'\"\n class=\"group-header\"\n (click)=\"toggleGroupExpansion(groupItem.group?.id || '')\"\n >\n <td class=\"checkbox-column\" style=\"border: none;\">\n {{ groupItem.group?.isExpanded ? '\u25BC' : '\u25B6' }}\n </td>\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'\" style=\"background:#fafafa\">\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 <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 <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 <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]=\"columns().length + 1\" 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 <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}.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"] }]
|
|
5246
5749
|
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { viewport: [{
|
|
5247
5750
|
type: ViewChild,
|
|
5248
5751
|
args: [CdkVirtualScrollViewport]
|
|
@@ -5255,6 +5758,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImpor
|
|
|
5255
5758
|
}], gtScroller: [{
|
|
5256
5759
|
type: ViewChild,
|
|
5257
5760
|
args: ['gtScroller', { read: ElementRef }]
|
|
5761
|
+
}], gridConfig: [{
|
|
5762
|
+
type: Input
|
|
5258
5763
|
}], vp: [{
|
|
5259
5764
|
type: ViewChild,
|
|
5260
5765
|
args: ['vp']
|