From 62baafb1b492e5cc3ace738cbe1b2b95d4dbeb3b Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Tue, 6 Jan 2026 23:31:26 -0500 Subject: [PATCH] feat(ExcelIO): add FluentTableWriter using map registry --- .../Generators/FluentTableWriter.cs | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 NEW/src/JdeScoping.ExcelIO/Generators/FluentTableWriter.cs diff --git a/NEW/src/JdeScoping.ExcelIO/Generators/FluentTableWriter.cs b/NEW/src/JdeScoping.ExcelIO/Generators/FluentTableWriter.cs new file mode 100644 index 0000000..14636f8 --- /dev/null +++ b/NEW/src/JdeScoping.ExcelIO/Generators/FluentTableWriter.cs @@ -0,0 +1,145 @@ +using ClosedXML.Excel; +using JdeScoping.ExcelIO.Formatting; +using JdeScoping.ExcelIO.Mapping; + +namespace JdeScoping.ExcelIO.Generators; + +/// +/// Writes Excel tables using fluent mapping configuration. +/// +public sealed class FluentTableWriter +{ + private readonly ExcelMapRegistry _registry; + + public FluentTableWriter(ExcelMapRegistry registry) + { + _registry = registry; + } + + /// + /// Writes a table to the worksheet using the registered map for type T. + /// + public IXLTable? WriteTable( + IXLWorksheet worksheet, + int startRow, + int startCol, + IEnumerable data, + string? tableNameOverride = null, + bool showHeader = false, + string? headerText = null) + { + var map = _registry.GetMap(); + 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 + }; + } +}