feat(ExcelIO): add FluentTableWriter using map registry

This commit is contained in:
Joseph Doherty
2026-01-06 23:31:26 -05:00
parent ccf70a9857
commit 62baafb1b4
@@ -0,0 +1,145 @@
using ClosedXML.Excel;
using JdeScoping.ExcelIO.Formatting;
using JdeScoping.ExcelIO.Mapping;
namespace JdeScoping.ExcelIO.Generators;
/// <summary>
/// Writes Excel tables using fluent mapping configuration.
/// </summary>
public sealed class FluentTableWriter
{
private readonly ExcelMapRegistry _registry;
public FluentTableWriter(ExcelMapRegistry registry)
{
_registry = registry;
}
/// <summary>
/// Writes a table to the worksheet using the registered map for type T.
/// </summary>
public IXLTable? WriteTable<T>(
IXLWorksheet worksheet,
int startRow,
int startCol,
IEnumerable<T> data,
string? tableNameOverride = null,
bool showHeader = false,
string? headerText = null)
{
var map = _registry.GetMap<T>();
var columns = map.Columns;
var tableName = tableNameOverride ?? map.TableName ?? typeof(T).Name;
var header = headerText ?? map.TabName ?? string.Empty;
if (columns.Count == 0)
return null;
var dataList = data.ToList();
var baseRow = startRow;
// Write merged header if requested
if (showHeader && !string.IsNullOrEmpty(header))
{
var mergedHeaderRange = worksheet.Range(baseRow, startCol, baseRow, startCol + columns.Count - 1);
HeaderFormatter.ApplyHeaderFormat(mergedHeaderRange, header, merge: true);
baseRow++;
}
// Write column headers
var col = startCol;
foreach (var column in columns)
{
var cell = worksheet.Cell(baseRow, col);
HeaderFormatter.ApplyHeaderFormat(cell, column.HeaderText);
// Pre-set column formatting
worksheet.Column(col).Style.Alignment.WrapText = column.WrapText;
if (!column.AutoWidth)
{
worksheet.Column(col).Width = column.Width;
}
col++;
}
// Write data rows
var row = baseRow + 1;
foreach (var item in dataList)
{
col = startCol;
foreach (var column in columns)
{
var value = column.ValueGetter(item!);
worksheet.Cell(row, col).Value = ConvertToXlValue(value);
col++;
}
row++;
}
// Handle empty data case
if (dataList.Count == 0)
{
row = baseRow + 1;
}
// Create table range
var dataRange = worksheet.Range(
baseRow, startCol,
baseRow + dataList.Count, startCol + columns.Count - 1);
// Create table
var table = dataRange.CreateTable(tableName);
table.Theme = XLTableTheme.TableStyleLight18;
table.ShowTotalsRow = false;
// Apply column formatting
col = startCol;
var tableStartRow = table.RangeAddress.FirstAddress.RowNumber;
var tableEndRow = table.RangeAddress.LastAddress.RowNumber;
foreach (var column in columns)
{
// Apply number format
worksheet.Range(tableStartRow, col, tableEndRow, col)
.Style.NumberFormat.Format = column.Format;
// Apply column width
if (column.WrapText && !column.AutoWidth)
{
worksheet.Column(col).Width = column.Width;
}
else if (column.AutoWidth)
{
worksheet.Column(col).AdjustToContents();
worksheet.Column(col).Width *= Formatting.ExcelFormats.DataPaddingFactor;
}
else
{
worksheet.Column(col).Width = column.Width;
}
col++;
}
return table;
}
private static XLCellValue ConvertToXlValue(object? value)
{
return value switch
{
null => Blank.Value,
string s => s,
int i => i,
long l => l,
decimal d => d,
double dbl => dbl,
float f => f,
DateTime dt => dt,
bool b => b,
_ => value.ToString() ?? string.Empty
};
}
}