# xllayout
Composable framework to build styled Excel reports with `openpyxl` and `matplotlib`.

## Philosophy
`xllayout` is split into three layers:
1. `Theme`: centralized visual tokens (fonts, fills, borders, number formats, spacing).
2. `Components`: reusable blocks (`TitleBlock`, `KpiCard`, `TableBlock`, `MatplotlibChart`, etc.).
3. `Layouts`: sheet composition using absolute canvas coordinates or `Grid12`.

This lets you place components at any coordinate without relying on cursor-based writing.

## Structure
```text
xllayout/
  pyproject.toml
  xllayout/
    __init__.py
    core/
      canvas.py
      grid.py
      theme.py
      formats.py
      utils.py
    components/
      title.py
      text.py
      section.py
      kpi.py
      table.py
      chart.py
    layouts/
      summary.py
    writer/
      workbook.py
  examples/
    build_summary.py
```

## Install
Editable (recommended for development):
```bash
pip install -e ".[charts]"
```

Standard install:
```bash
pip install ".[charts]"
```

- Base dependency: `openpyxl`
- Optional extra `charts`: `matplotlib`, `Pillow`

## Quick API
```python
from xllayout.core.theme import LightTheme
from xllayout.layouts.summary import ExecutiveSummaryData
from xllayout.writer.workbook import ReportWriter

writer = ReportWriter(theme=LightTheme(), detect_collisions=True)
wb = writer.new_workbook()
writer.build_summary_sheet(wb, data)  # data: ExecutiveSummaryData
wb.save("report.xlsx")
```

## Table Cell Styling (v1.1.0)
`TableBlock` now supports per-column text/cell styling:

```python
from xllayout.components.table import TableBlock
from xllayout.core.cell_style import CellStyleSpec

table = TableBlock(
    headers=["id", "descripcion", "valor"],
    rows=[[1, "Texto largo que debe envolver en varias lineas", 1200]],
    col_widths=[10, 34, 14],
    number_formats_by_col={3: "#,##0"},
    body_style_by_col={
        1: CellStyleSpec(align_horizontal="center", bold=True),
        2: CellStyleSpec(wrap_text=True, align_vertical="top"),
        3: CellStyleSpec(align_horizontal="right", font_color="#1B4D8A"),
    },
    header_style=CellStyleSpec(
        bold=True,
        align_horizontal="center",
        fill_color="#E2EAF3",
        border_style="thin",
        border_color="#D0D7DE",
    ),
    header_row_height=24,
    default_row_height=32,
)
```

Supported style attributes (`CellStyleSpec`):
- font: `font_name`, `font_size`, `bold`, `italic`, `underline`, `font_color`
- cell: `fill_color`, `border_style`, `border_color`
- alignment: `align_horizontal`, `align_vertical`, `wrap_text`, `shrink_to_fit`, `text_rotation`
- format: `number_format`

## Canvas vs Grid12
Absolute placement:
```python
from xllayout.core.canvas import SheetCanvas

canvas = SheetCanvas(ws, theme, detect_collisions=True)
canvas.put(component, row=5, col=3)
```

Grid placement:
```python
from xllayout.core.grid import Grid12

grid = Grid12(canvas)  # 12 tracks
grid.put(component, y=4, x=1, w=6, h=5)
```

If `detect_collisions=True`, overlapping components raise `ValueError`.

## Build a New Layout
1. Create a typed input dataclass under `layouts/`.
2. In `build(ws)`, initialize `SheetCanvas` and optionally `Grid12`.
3. Place components with `canvas.put(...)` or `grid.put(...)`.
4. Expose a facade method in `ReportWriter`.

## Run the example
```bash
python examples/build_summary.py
```

Output:
```text
examples/output/summary_report_example.xlsx
```

## Versioning and publishing
- Uses SemVer (`1.0.0`, `1.0.1`, `1.1.0`, ...).
- Recommended for teams: publish to a private PyPI-compatible registry.
