Metadata-Version: 2.4
Name: xllayout
Version: 1.2.1
Summary: Composable framework for building styled Excel reports with openpyxl and matplotlib.
Author: Juan Román Hernández
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: openpyxl<4,>=3.1
Provides-Extra: charts
Requires-Dist: matplotlib<4,>=3.8; extra == "charts"
Requires-Dist: Pillow<12,>=10; extra == "charts"

# 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
      cell_style.py
      grid.py
      style_sheet.py
      style_engine.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.core.style_sheet import StyleSheet
from xllayout.layouts.summary import ExecutiveSummaryData
from xllayout.writer.workbook import ReportWriter

style_sheet = StyleSheet(
    styles={
        "title-block.subtitle": {"font_color": "#5B6570", "font_size": 9},
        "section-block.text": {"fill_color": "#E8EEF5", "bold": True},
        "kpi-card.value": {"font_size": 20, "bold": True, "font_color": "#0F4C81"},
        "table-block.header": {"align_horizontal": "center", "wrap_text": True},
    }
)

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

## Style Tag + Local Override (v1.2.0)
Each component supports:
- `style="..."`: global style tags (like CSS classes)
- `style_props={...}`: local overrides for that instance

`style_props` supports either:
- flat dict for the container (`{"fill_color": "...", "border_style": "thin"}`)
- per-part dict (`{"header": {...}, "body": {...}, "value": {...}}`)

Example:

```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"},
    style="table-block compact",
    style_props={
        "header": {"fill_color": "#E2EAF3"},
        "body": {"align_vertical": "center"},
    },
    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`, `1.2.0`, ...).
- Recommended for teams: publish to a private PyPI-compatible registry.
