@vii7/div-table-widget 1.1.1 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/div-table.js CHANGED
@@ -113,6 +113,20 @@ class DivTable {
113
113
  }
114
114
  }
115
115
 
116
+ /**
117
+ * Strip HTML tags from a string for use in tooltips
118
+ * @param {string} html - String potentially containing HTML
119
+ * @returns {string} Plain text without HTML tags
120
+ */
121
+ stripHtmlTags(html) {
122
+ if (!html) return '';
123
+ // Replace <br> and <br/> with space, then strip all other HTML tags
124
+ return String(html)
125
+ .replace(/<br\s*\/?>/gi, ' ')
126
+ .replace(/<[^>]*>/g, '')
127
+ .trim();
128
+ }
129
+
116
130
  init() {
117
131
  const container = this.options.tableWidgetElement;
118
132
  if (!container) {
@@ -1712,12 +1726,21 @@ class DivTable {
1712
1726
  fixedRow.style.height = '';
1713
1727
  scrollRow.style.height = '';
1714
1728
 
1715
- // Get natural heights
1716
- const fixedHeight = fixedRow.offsetHeight;
1717
- const scrollHeight = scrollRow.offsetHeight;
1729
+ // Get natural heights including any cell content overflow
1730
+ const fixedHeight = Math.max(fixedRow.offsetHeight, fixedRow.scrollHeight);
1731
+ const scrollHeight = Math.max(scrollRow.offsetHeight, scrollRow.scrollHeight);
1732
+
1733
+ // Also check individual cell heights
1734
+ let maxCellHeight = 0;
1735
+ fixedRow.querySelectorAll('.div-table-cell').forEach(cell => {
1736
+ maxCellHeight = Math.max(maxCellHeight, cell.offsetHeight, cell.scrollHeight);
1737
+ });
1738
+ scrollRow.querySelectorAll('.div-table-cell').forEach(cell => {
1739
+ maxCellHeight = Math.max(maxCellHeight, cell.offsetHeight, cell.scrollHeight);
1740
+ });
1718
1741
 
1719
1742
  // Set both to the maximum height
1720
- const maxHeight = Math.max(fixedHeight, scrollHeight);
1743
+ const maxHeight = Math.max(fixedHeight, scrollHeight, maxCellHeight);
1721
1744
  if (maxHeight > 0) {
1722
1745
  fixedRow.style.height = `${maxHeight}px`;
1723
1746
  scrollRow.style.height = `${maxHeight}px`;
@@ -1892,8 +1915,8 @@ class DivTable {
1892
1915
  const mainLabel = document.createElement('span');
1893
1916
  mainLabel.className = 'composite-main-header';
1894
1917
  mainLabel.innerHTML = col.label || col.field;
1918
+ mainLabel.title = this.stripHtmlTags(col.label || col.field);
1895
1919
  mainLabel.style.fontWeight = '600';
1896
- mainLabel.style.color = '#374151';
1897
1920
  mainLabel.style.textAlign = 'left';
1898
1921
  mainLabel.style.flex = '1';
1899
1922
  mainLabelContainer.appendChild(mainLabel);
@@ -1916,7 +1939,7 @@ class DivTable {
1916
1939
  groupIndicator.textContent = this.groupByField === col.field ? '☴' : '☷';
1917
1940
  groupIndicator.style.cursor = 'pointer';
1918
1941
  groupIndicator.style.fontSize = '1em';
1919
- const columnTitle = col.label || col.field;
1942
+ const columnTitle = this.stripHtmlTags(col.label || col.field);
1920
1943
  groupIndicator.title = this.groupByField === col.field ? `Grouped by ${columnTitle} (click to ungroup)` : `Click to group by ${columnTitle}`;
1921
1944
 
1922
1945
  // Add click handler for grouping
@@ -1960,7 +1983,7 @@ class DivTable {
1960
1983
 
1961
1984
  const subLabel = document.createElement('span');
1962
1985
  subLabel.innerHTML = col.subLabel;
1963
- subLabel.style.color = '#6b7280';
1986
+ subLabel.title = this.stripHtmlTags(col.subLabel);
1964
1987
  subLabel.style.textAlign = 'left';
1965
1988
  subLabel.style.flex = '1';
1966
1989
  subLabelContainer.appendChild(subLabel);
@@ -1980,9 +2003,9 @@ class DivTable {
1980
2003
 
1981
2004
  subLabelContainer.appendChild(subSortIndicator);
1982
2005
 
1983
- // Add hover effect
2006
+ // Add hover effect - use CSS variable for theming support
1984
2007
  subLabelContainer.addEventListener('mouseenter', () => {
1985
- subLabelContainer.style.backgroundColor = '#f3f4f6';
2008
+ subLabelContainer.style.backgroundColor = 'var(--dt-bg-disabled)';
1986
2009
  });
1987
2010
  subLabelContainer.addEventListener('mouseleave', () => {
1988
2011
  subLabelContainer.style.backgroundColor = 'transparent';
@@ -2053,6 +2076,7 @@ class DivTable {
2053
2076
  // Column label
2054
2077
  const labelSpan = document.createElement('span');
2055
2078
  labelSpan.innerHTML = columnLabel;
2079
+ labelSpan.title = this.stripHtmlTags(columnLabel);
2056
2080
  //labelSpan.style.fontWeight = 'bold';
2057
2081
  leftContent.appendChild(labelSpan);
2058
2082
 
@@ -2063,12 +2087,13 @@ class DivTable {
2063
2087
  countSpan.style.opacity = '0.8';
2064
2088
  countSpan.style.fontSize = '0.9em';
2065
2089
  countSpan.style.fontWeight = 'normal';
2066
- countSpan.title = `${groupCount} distinct value${groupCount === 1 ? '' : 's'} in ${columnLabel}`;
2090
+ countSpan.title = `${groupCount} distinct value${groupCount === 1 ? '' : 's'} in ${this.stripHtmlTags(columnLabel)}`;
2067
2091
  leftContent.appendChild(countSpan);
2068
2092
  } else {
2069
2093
  // Regular column label
2070
2094
  const labelSpan = document.createElement('span');
2071
2095
  labelSpan.innerHTML = col.label || col.field;
2096
+ labelSpan.title = this.stripHtmlTags(col.label || col.field);
2072
2097
  //labelSpan.style.fontWeight = 'bold';
2073
2098
  leftContent.appendChild(labelSpan);
2074
2099
  }
@@ -2089,7 +2114,7 @@ class DivTable {
2089
2114
  groupIndicator.textContent = this.groupByField === col.field ? '☴' : '☷';
2090
2115
  groupIndicator.style.cursor = 'pointer';
2091
2116
  groupIndicator.style.fontSize = '1em';
2092
- const columnTitle = col.label || col.field;
2117
+ const columnTitle = this.stripHtmlTags(col.label || col.field);
2093
2118
  groupIndicator.title = this.groupByField === col.field ? `Grouped by ${columnTitle} (click to ungroup)` : `Click to group by ${columnTitle}`;
2094
2119
 
2095
2120
  // Add click handler for grouping
@@ -2215,6 +2240,7 @@ class DivTable {
2215
2240
  // Column label
2216
2241
  const labelSpan = document.createElement('span');
2217
2242
  labelSpan.innerHTML = columnLabel;
2243
+ labelSpan.title = this.stripHtmlTags(columnLabel);
2218
2244
  leftContent.appendChild(labelSpan);
2219
2245
 
2220
2246
  // Group count
@@ -2224,12 +2250,13 @@ class DivTable {
2224
2250
  groupCountSpan.style.opacity = '0.8';
2225
2251
  groupCountSpan.style.fontSize = '0.9em';
2226
2252
  groupCountSpan.style.fontWeight = 'normal';
2227
- groupCountSpan.title = `${groupCount} distinct value${groupCount === 1 ? '' : 's'} in ${columnLabel}`;
2253
+ groupCountSpan.title = `${groupCount} distinct value${groupCount === 1 ? '' : 's'} in ${this.stripHtmlTags(columnLabel)}`;
2228
2254
  leftContent.appendChild(groupCountSpan);
2229
2255
  } else {
2230
2256
  // Regular label (not grouped)
2231
2257
  const labelSpan = document.createElement('span');
2232
2258
  labelSpan.innerHTML = col.label || col.field;
2259
+ labelSpan.title = this.stripHtmlTags(col.label || col.field);
2233
2260
  leftContent.appendChild(labelSpan);
2234
2261
  }
2235
2262
 
@@ -2251,7 +2278,7 @@ class DivTable {
2251
2278
  groupIndicator.textContent = this.groupByField === col.field ? '☴' : '☷';
2252
2279
  groupIndicator.style.cursor = 'pointer';
2253
2280
  groupIndicator.style.fontSize = '1em';
2254
- const columnTitle = col.label || col.field;
2281
+ const columnTitle = this.stripHtmlTags(col.label || col.field);
2255
2282
  groupIndicator.title = this.groupByField === col.field ? `Grouped by ${columnTitle} (click to ungroup)` : `Click to group by ${columnTitle}`;
2256
2283
 
2257
2284
  groupIndicator.addEventListener('click', (e) => {
@@ -2758,120 +2785,15 @@ class DivTable {
2758
2785
  row.appendChild(checkboxCell);
2759
2786
  }
2760
2787
 
2761
- // Data columns - render using composite structure
2788
+ // Data columns - render using composite structure with proper alignment
2762
2789
  compositeColumns.forEach(composite => {
2763
- const cell = document.createElement('div');
2764
- cell.className = 'div-table-cell';
2765
-
2766
- if (composite.compositeName) {
2767
- cell.classList.add('composite-cell');
2768
- cell.style.display = 'flex';
2769
- cell.style.flexDirection = 'column';
2770
- cell.style.gap = '4px';
2771
-
2772
- composite.columns.forEach((col, index) => {
2773
- const subCell = document.createElement('div');
2774
- subCell.className = 'composite-sub-cell';
2775
-
2776
- if (this.groupByField && col.field === this.groupByField) {
2777
- subCell.classList.add('grouped-column');
2778
- subCell.textContent = '';
2779
- } else {
2780
- if (col.subField) {
2781
- subCell.classList.add('composite-column');
2782
- subCell.style.display = 'flex';
2783
- subCell.style.flexDirection = 'column';
2784
- subCell.style.gap = '2px';
2785
-
2786
- const mainDiv = document.createElement('div');
2787
- mainDiv.className = 'composite-main';
2788
- if (typeof col.render === 'function') {
2789
- mainDiv.innerHTML = col.render(item[col.field], item);
2790
- } else {
2791
- mainDiv.innerHTML = item[col.field] ?? '';
2792
- }
2793
-
2794
- const subDiv = document.createElement('div');
2795
- subDiv.className = 'composite-sub';
2796
- if (typeof col.subRender === 'function') {
2797
- subDiv.innerHTML = col.subRender(item[col.subField], item);
2798
- } else {
2799
- subDiv.innerHTML = item[col.subField] ?? '';
2800
- }
2801
-
2802
- subCell.appendChild(mainDiv);
2803
- subCell.appendChild(subDiv);
2804
- } else {
2805
- if (typeof col.render === 'function') {
2806
- subCell.innerHTML = col.render(item[col.field], item);
2807
- } else {
2808
- subCell.innerHTML = item[col.field] ?? '';
2809
- }
2810
- }
2811
- }
2812
-
2813
- cell.appendChild(subCell);
2814
- });
2815
- } else {
2816
- const col = composite.columns[0];
2817
-
2818
- if (this.groupByField && col.field === this.groupByField) {
2819
- cell.classList.add('grouped-column');
2820
- cell.textContent = '';
2821
- } else {
2822
- if (col.subField) {
2823
- cell.classList.add('composite-column');
2824
-
2825
- const mainDiv = document.createElement('div');
2826
- mainDiv.className = 'composite-main';
2827
- if (typeof col.render === 'function') {
2828
- mainDiv.innerHTML = col.render(item[col.field], item);
2829
- } else {
2830
- mainDiv.innerHTML = item[col.field] ?? '';
2831
- }
2832
-
2833
- const subDiv = document.createElement('div');
2834
- subDiv.className = 'composite-sub';
2835
- if (typeof col.subRender === 'function') {
2836
- subDiv.innerHTML = col.subRender(item[col.subField], item);
2837
- } else {
2838
- subDiv.innerHTML = item[col.subField] ?? '';
2839
- }
2840
-
2841
- cell.appendChild(mainDiv);
2842
- cell.appendChild(subDiv);
2843
- } else {
2844
- if (typeof col.render === 'function') {
2845
- cell.innerHTML = col.render(item[col.field], item);
2846
- } else {
2847
- cell.innerHTML = item[col.field] ?? '';
2848
- }
2849
- }
2850
- }
2851
- }
2852
-
2790
+ const cell = this.createCellForComposite(composite, item);
2853
2791
  row.appendChild(cell);
2854
2792
  });
2855
2793
 
2856
2794
  // Mark as populated
2857
2795
  row.dataset.populated = 'true';
2858
2796
 
2859
- // Measure actual height and set it explicitly to prevent bouncing on re-renders
2860
- // Use requestAnimationFrame to ensure DOM has updated
2861
- requestAnimationFrame(() => {
2862
- const actualHeight = row.offsetHeight;
2863
- if (actualHeight > 0) {
2864
- // Set explicit height to prevent layout recalculation bouncing
2865
- row.style.minHeight = `${actualHeight}px`;
2866
- row.style.height = `${actualHeight}px`;
2867
-
2868
- // Update estimate for future unpopulated rows
2869
- if (actualHeight > this.estimatedRowHeight) {
2870
- this.estimatedRowHeight = actualHeight;
2871
- }
2872
- }
2873
- });
2874
-
2875
2797
  // Update tab indexes after population
2876
2798
  this.updateTabIndexes();
2877
2799
  }
@@ -2981,32 +2903,28 @@ class DivTable {
2981
2903
  // Use double requestAnimationFrame to ensure layout is fully complete
2982
2904
  requestAnimationFrame(() => {
2983
2905
  requestAnimationFrame(() => {
2984
- // Get natural heights after cell content is fully rendered
2985
- // Temporarily remove any height constraints to get true natural height
2986
- const prevFixedHeight = fixedRow.style.height;
2987
- const prevScrollHeight = scrollRow.style.height;
2988
- const prevFixedMinHeight = fixedRow.style.minHeight;
2989
- const prevScrollMinHeight = scrollRow.style.minHeight;
2990
-
2906
+ // Reset any fixed heights to get natural content height
2991
2907
  fixedRow.style.height = '';
2992
2908
  scrollRow.style.height = '';
2993
- fixedRow.style.minHeight = '40px';
2994
- scrollRow.style.minHeight = '40px';
2995
2909
 
2996
- // Force layout recalculation
2997
- const fixedHeight = fixedRow.offsetHeight;
2998
- const scrollHeight = scrollRow.offsetHeight;
2910
+ // Get the maximum height from both rows, including any cell content
2911
+ // Use scrollHeight to capture content that might overflow
2912
+ const fixedHeight = Math.max(fixedRow.offsetHeight, fixedRow.scrollHeight);
2913
+ const scrollHeight = Math.max(scrollRow.offsetHeight, scrollRow.scrollHeight);
2999
2914
 
3000
- // Set both to the maximum height to keep rows in sync
3001
- const maxHeight = Math.max(fixedHeight, scrollHeight, 40); // Ensure minimum 44px
3002
- fixedRow.style.minHeight = `${maxHeight}px`;
3003
- fixedRow.style.height = `${maxHeight}px`;
3004
- scrollRow.style.minHeight = `${maxHeight}px`;
3005
- scrollRow.style.height = `${maxHeight}px`;
2915
+ // Also check individual cell heights
2916
+ let maxCellHeight = 0;
2917
+ fixedRow.querySelectorAll('.div-table-cell').forEach(cell => {
2918
+ maxCellHeight = Math.max(maxCellHeight, cell.offsetHeight, cell.scrollHeight);
2919
+ });
2920
+ scrollRow.querySelectorAll('.div-table-cell').forEach(cell => {
2921
+ maxCellHeight = Math.max(maxCellHeight, cell.offsetHeight, cell.scrollHeight);
2922
+ });
3006
2923
 
3007
- // Update estimate for future unpopulated rows
3008
- if (maxHeight > this.estimatedRowHeight) {
3009
- this.estimatedRowHeight = maxHeight;
2924
+ const maxHeight = Math.max(fixedHeight, scrollHeight, maxCellHeight);
2925
+ if (maxHeight > 0) {
2926
+ fixedRow.style.height = `${maxHeight}px`;
2927
+ scrollRow.style.height = `${maxHeight}px`;
3010
2928
  }
3011
2929
  });
3012
2930
  });
@@ -3198,106 +3116,9 @@ class DivTable {
3198
3116
  row.appendChild(checkboxCell);
3199
3117
  }
3200
3118
 
3201
- // Data columns - render using composite structure
3119
+ // Data columns - render using composite structure with proper alignment
3202
3120
  compositeColumns.forEach(composite => {
3203
- const cell = document.createElement('div');
3204
- cell.className = 'div-table-cell';
3205
-
3206
- if (composite.compositeName) {
3207
- // Composite cell with multiple columns stacked vertically
3208
- cell.classList.add('composite-cell');
3209
- cell.style.display = 'flex';
3210
- cell.style.flexDirection = 'column';
3211
- cell.style.gap = '4px';
3212
-
3213
- composite.columns.forEach((col, index) => {
3214
- const subCell = document.createElement('div');
3215
- subCell.className = 'composite-sub-cell';
3216
-
3217
- // For grouped column, show empty
3218
- if (this.groupByField && col.field === this.groupByField) {
3219
- subCell.classList.add('grouped-column');
3220
- subCell.textContent = '';
3221
- } else {
3222
- // Check if this column has subField (vertical stacking within the sub-cell)
3223
- if (col.subField) {
3224
- subCell.classList.add('composite-column');
3225
- subCell.style.display = 'flex';
3226
- subCell.style.flexDirection = 'column';
3227
- subCell.style.gap = '2px';
3228
-
3229
- const mainDiv = document.createElement('div');
3230
- mainDiv.className = 'composite-main';
3231
- if (typeof col.render === 'function') {
3232
- mainDiv.innerHTML = col.render(item[col.field], item);
3233
- } else {
3234
- mainDiv.innerHTML = item[col.field] ?? '';
3235
- }
3236
-
3237
- const subDiv = document.createElement('div');
3238
- subDiv.className = 'composite-sub';
3239
- if (typeof col.subRender === 'function') {
3240
- subDiv.innerHTML = col.subRender(item[col.subField], item);
3241
- } else {
3242
- subDiv.innerHTML = item[col.subField] ?? '';
3243
- }
3244
-
3245
- subCell.appendChild(mainDiv);
3246
- subCell.appendChild(subDiv);
3247
- } else {
3248
- // Regular rendering
3249
- if (typeof col.render === 'function') {
3250
- subCell.innerHTML = col.render(item[col.field], item);
3251
- } else {
3252
- subCell.innerHTML = item[col.field] ?? '';
3253
- }
3254
- }
3255
- }
3256
-
3257
- cell.appendChild(subCell);
3258
- });
3259
- } else {
3260
- // Single column
3261
- const col = composite.columns[0];
3262
-
3263
- // For grouped column, show empty
3264
- if (this.groupByField && col.field === this.groupByField) {
3265
- cell.classList.add('grouped-column');
3266
- cell.textContent = '';
3267
- } else {
3268
- // Check if this is a composite column with subField (vertical stacking)
3269
- if (col.subField) {
3270
- cell.classList.add('composite-column');
3271
-
3272
- const mainDiv = document.createElement('div');
3273
- mainDiv.className = 'composite-main';
3274
- if (typeof col.render === 'function') {
3275
- mainDiv.innerHTML = col.render(item[col.field], item);
3276
- } else {
3277
- mainDiv.innerHTML = item[col.field] ?? '';
3278
- }
3279
-
3280
- const subDiv = document.createElement('div');
3281
- subDiv.className = 'composite-sub';
3282
- if (typeof col.subRender === 'function') {
3283
- subDiv.innerHTML = col.subRender(item[col.subField], item);
3284
- } else {
3285
- subDiv.innerHTML = item[col.subField] ?? '';
3286
- }
3287
-
3288
- cell.appendChild(mainDiv);
3289
- cell.appendChild(subDiv);
3290
- } else {
3291
- // Regular column rendering
3292
- if (typeof col.render === 'function') {
3293
- cell.innerHTML = col.render(item[col.field], item);
3294
- } else {
3295
- cell.innerHTML = item[col.field] ?? '';
3296
- }
3297
- }
3298
- }
3299
- }
3300
-
3121
+ const cell = this.createCellForComposite(composite, item);
3301
3122
  row.appendChild(cell);
3302
3123
  });
3303
3124
 
@@ -3586,9 +3407,6 @@ class DivTable {
3586
3407
  if (composite.compositeName) {
3587
3408
  // Composite cell with multiple columns stacked vertically
3588
3409
  cell.classList.add('composite-cell');
3589
- cell.style.display = 'flex';
3590
- cell.style.flexDirection = 'column';
3591
- cell.style.gap = '4px';
3592
3410
 
3593
3411
  composite.columns.forEach((col, index) => {
3594
3412
  const subCell = document.createElement('div');
@@ -3611,6 +3429,8 @@ class DivTable {
3611
3429
  } else {
3612
3430
  mainDiv.innerHTML = item[col.field] ?? '';
3613
3431
  }
3432
+ // Set title for tooltip on main content
3433
+ mainDiv.title = this.stripHtmlTags(mainDiv.innerHTML);
3614
3434
 
3615
3435
  const subDiv = document.createElement('div');
3616
3436
  subDiv.className = 'composite-sub';
@@ -3619,6 +3439,8 @@ class DivTable {
3619
3439
  } else {
3620
3440
  subDiv.innerHTML = item[col.subField] ?? '';
3621
3441
  }
3442
+ // Set title for tooltip on sub content
3443
+ subDiv.title = this.stripHtmlTags(subDiv.innerHTML);
3622
3444
 
3623
3445
  subCell.appendChild(mainDiv);
3624
3446
  subCell.appendChild(subDiv);
@@ -3628,6 +3450,8 @@ class DivTable {
3628
3450
  } else {
3629
3451
  subCell.innerHTML = item[col.field] ?? '';
3630
3452
  }
3453
+ // Set title for tooltip on sub-cell
3454
+ subCell.title = this.stripHtmlTags(subCell.innerHTML);
3631
3455
  }
3632
3456
  }
3633
3457
 
@@ -3651,6 +3475,8 @@ class DivTable {
3651
3475
  } else {
3652
3476
  mainDiv.innerHTML = item[col.field] ?? '';
3653
3477
  }
3478
+ // Set title for tooltip on main content
3479
+ mainDiv.title = this.stripHtmlTags(mainDiv.innerHTML);
3654
3480
 
3655
3481
  const subDiv = document.createElement('div');
3656
3482
  subDiv.className = 'composite-sub';
@@ -3659,15 +3485,23 @@ class DivTable {
3659
3485
  } else {
3660
3486
  subDiv.innerHTML = item[col.subField] ?? '';
3661
3487
  }
3488
+ // Set title for tooltip on sub content
3489
+ subDiv.title = this.stripHtmlTags(subDiv.innerHTML);
3662
3490
 
3663
3491
  cell.appendChild(mainDiv);
3664
3492
  cell.appendChild(subDiv);
3665
3493
  } else {
3494
+ // Wrap content in a span for proper flex alignment
3495
+ const contentSpan = document.createElement('span');
3496
+ contentSpan.className = 'cell-content';
3666
3497
  if (typeof col.render === 'function') {
3667
- cell.innerHTML = col.render(item[col.field], item);
3498
+ contentSpan.innerHTML = col.render(item[col.field], item);
3668
3499
  } else {
3669
- cell.innerHTML = item[col.field] ?? '';
3500
+ contentSpan.innerHTML = item[col.field] ?? '';
3670
3501
  }
3502
+ // Set title for tooltip
3503
+ contentSpan.title = this.stripHtmlTags(contentSpan.innerHTML);
3504
+ cell.appendChild(contentSpan);
3671
3505
  }
3672
3506
  }
3673
3507
  }
@@ -5858,6 +5692,10 @@ class DivTable {
5858
5692
  const formattedValue = this.formatAggregateValue(aggregateValue, col);
5859
5693
  subCell.innerHTML = formattedValue;
5860
5694
  subCell.classList.add('aggregate-value');
5695
+ // Apply column alignment
5696
+ if (col.align) {
5697
+ subCell.style.textAlign = col.align;
5698
+ }
5861
5699
  }
5862
5700
 
5863
5701
  cell.appendChild(subCell);
@@ -5866,10 +5704,21 @@ class DivTable {
5866
5704
  // Single column
5867
5705
  const col = composite.columns[0];
5868
5706
 
5707
+ // Apply column alignment
5708
+ if (col.align) {
5709
+ cell.style.textAlign = col.align;
5710
+ cell.style.justifyContent = col.align === 'right' ? 'flex-end' :
5711
+ col.align === 'center' ? 'center' : 'flex-start';
5712
+ }
5713
+
5869
5714
  if (col.aggregate) {
5870
5715
  const aggregateValue = this.calculateAggregate(col, aggregationData);
5871
5716
  const formattedValue = this.formatAggregateValue(aggregateValue, col);
5872
- cell.innerHTML = formattedValue;
5717
+ // Wrap in span for proper flex alignment
5718
+ const contentSpan = document.createElement('span');
5719
+ contentSpan.className = 'cell-content';
5720
+ contentSpan.innerHTML = formattedValue;
5721
+ cell.appendChild(contentSpan);
5873
5722
  cell.classList.add('aggregate-value');
5874
5723
  }
5875
5724
  }
@@ -5947,6 +5796,10 @@ class DivTable {
5947
5796
  const formattedValue = this.formatAggregateValue(aggregateValue, col);
5948
5797
  subCell.innerHTML = formattedValue;
5949
5798
  subCell.classList.add('aggregate-value');
5799
+ // Apply column alignment
5800
+ if (col.align) {
5801
+ subCell.style.textAlign = col.align;
5802
+ }
5950
5803
  }
5951
5804
 
5952
5805
  cell.appendChild(subCell);
@@ -5954,10 +5807,21 @@ class DivTable {
5954
5807
  } else {
5955
5808
  const col = composite.columns[0];
5956
5809
 
5810
+ // Apply column alignment
5811
+ if (col.align) {
5812
+ cell.style.textAlign = col.align;
5813
+ cell.style.justifyContent = col.align === 'right' ? 'flex-end' :
5814
+ col.align === 'center' ? 'center' : 'flex-start';
5815
+ }
5816
+
5957
5817
  if (col.aggregate) {
5958
5818
  const aggregateValue = this.calculateAggregate(col, groupData);
5959
5819
  const formattedValue = this.formatAggregateValue(aggregateValue, col);
5960
- cell.innerHTML = formattedValue;
5820
+ // Wrap in span for proper flex alignment
5821
+ const contentSpan = document.createElement('span');
5822
+ contentSpan.className = 'cell-content';
5823
+ contentSpan.innerHTML = formattedValue;
5824
+ cell.appendChild(contentSpan);
5961
5825
  cell.classList.add('aggregate-value');
5962
5826
  }
5963
5827
  }
@@ -6135,7 +5999,11 @@ class DivTable {
6135
5999
  if (col.aggregate) {
6136
6000
  const aggregateValue = this.calculateAggregate(col, data);
6137
6001
  const formattedValue = this.formatAggregateValue(aggregateValue, col);
6138
- cell.innerHTML = formattedValue;
6002
+ // Wrap in span for proper flex alignment
6003
+ const contentSpan = document.createElement('span');
6004
+ contentSpan.className = 'cell-content';
6005
+ contentSpan.innerHTML = formattedValue;
6006
+ cell.appendChild(contentSpan);
6139
6007
  cell.classList.add('aggregate-value');
6140
6008
  }
6141
6009
  }