# fh-echarts


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

## Install

``` sh
pip install fh-echarts
```

Or install latest from GitHub:

``` sh
pip install git+https://github.com/markkvdb/fh-echarts.git
```

## Quick Start

Add `echarts_header()` to your FastHTML app headers, then use `EChart()`
to render charts:

``` python
from fasthtml.common import *
from fh_echarts.core import echarts_header, EChart

app, rt = fast_app(hdrs=(echarts_header(),))

@rt('/')
def get():
    options = {
        "xAxis": {"type": "category", "data": ["Mon", "Tue", "Wed", "Thu", "Fri"]},
        "yAxis": {"type": "value"},
        "series": [{"data": [120, 200, 150, 80, 70], "type": "bar"}]
    }
    return Titled("My Chart", EChart(options))

serve()
```

## Features

### Basic Bar Chart

Pass an ECharts option dict to `EChart()`. Use `preview_echart()` to
preview charts in notebooks.

``` python
options = {
    "xAxis": {"type": "category", "data": ["Mon", "Tue", "Wed", "Thu", "Fri"]},
    "yAxis": {"type": "value"},
    "series": [{"data": [120, 200, 150, 80, 70], "type": "bar"}]
}
preview_echart(EChart(options, chart_id="idx_bar"))
```

### Dark Theme

Pass `theme="dark"` (or `"light"`) to use ECharts’ built-in themes.

``` python
options_dark = {
    "title": {"text": "Dark Theme"},
    "xAxis": {"data": ["A", "B", "C", "D"]},
    "yAxis": {},
    "series": [{"type": "bar", "data": [10, 25, 15, 30]}]
}
preview_echart(EChart(options_dark, chart_id="idx_dark", theme="dark"))
```

### JavaScript Functions with `JSFunc`

ECharts uses JavaScript functions for custom tooltips, axis labels, etc.
Normally
[`json.dumps`](https://docs.python.org/3/library/json.html#json.dumps)
would turn these into literal strings. Wrap them in `JSFunc()` and
they’ll be revived as real JS functions in the browser.

``` python
options_fmt = {
    "title": {"text": "Custom Tooltip"},
    "tooltip": {
        "formatter": JSFunc("function(p) { return '<b>' + p.name + '</b>: $' + p.value.toLocaleString(); }")
    },
    "xAxis": {"data": ["Shirts", "Sweaters", "Hats", "Shoes"]},
    "yAxis": {"axisLabel": {
        "formatter": JSFunc("function(v) { return '$' + v; }")
    }},
    "series": [{"type": "bar", "data": [500, 2000, 360, 1200]}]
}
preview_echart(EChart(options_fmt, chart_id="idx_fmt", theme="dark"))
```

### Line and Pie Charts

Any ECharts chart type works — just set `"type"` in the series.

``` python
line_opts = {
    "title": {"text": "Temperature (°C)"},
    "xAxis": {"type": "category", "data": ["Jan", "Feb", "Mar", "Apr", "May", "Jun"]},
    "yAxis": {"type": "value", "axisLabel": {
        "formatter": JSFunc("function(v) { return v + '°C'; }")
    }},
    "series": [{"type": "line", "data": [2, 5, 12, 18, 24, 28], "smooth": True}]
}
preview_echart(EChart(line_opts, chart_id="idx_line"))
```

``` python
pie_opts = {
    "title": {"text": "Browser Share", "left": "center"},
    "tooltip": {
        "formatter": JSFunc("function(p) { return p.name + ': ' + p.percent + '%'; }")
    },
    "series": [{
        "type": "pie", "radius": "60%",
        "data": [
            {"value": 65, "name": "Chrome"},
            {"value": 18, "name": "Safari"},
            {"value": 10, "name": "Firefox"},
            {"value":  7, "name": "Other"}
        ]
    }]
}
preview_echart(EChart(pie_opts, chart_id="idx_pie"))
```

### HTMX Click Integration

Turn chart clicks into server requests with `hx_get_click`. When a user
clicks a data point, an HTMX GET request is fired with `name`, `value`,
and `seriesName` as query parameters.

``` python
@rt('/')
def get():
    options = {
        "xAxis": {"data": ["Jan", "Feb", "Mar"]},
        "yAxis": {},
        "series": [{"name": "Revenue", "type": "bar", "data": [100, 200, 150]}]
    }
    return Div(
        EChart(options,
               hx_get_click="/bar-clicked",
               hx_target_click="#result"),
        Div(id="result")
    )

@rt('/bar-clicked')
def get(name: str, value: int, seriesName: str):
    return P(f"Clicked {name} ({seriesName}): {value}")
```

### Dynamic Updates with `EChartUpdate`

Update an existing chart without re-creating it. Return an
`EChartUpdate` from a route to merge new options into the chart.

``` python
import random

@rt('/')
def get():
    options = {
        "xAxis": {"data": ["A", "B", "C"]},
        "yAxis": {},
        "series": [{"type": "bar", "data": [10, 20, 30]}]
    }
    return Div(
        EChart(options, chart_id="updatable"),
        Button("Randomize", hx_get="/randomize", hx_target="#update-slot"),
        Div(id="update-slot")
    )

@rt('/randomize')
def get():
    new_data = [random.randint(5, 100) for _ in range(3)]
    return EChartUpdate("updatable", {"series": [{"data": new_data}]})
```

Set `merge=False` to replace all options instead of merging.

### Memory Cleanup

When HTMX removes a chart from the DOM (e.g. navigating tabs or swapping
content), the ECharts instance and `ResizeObserver` are automatically
disposed via the `htmx:beforeCleanupElement` event. No extra code
needed.

## API Reference

| Function | Description |
|----|----|
| `echarts_header(version)` | CDN script tag for ECharts (default v5.5.0) |
| `EChart(options, ...)` | Render a chart with optional `theme`, `hx_get_click`, `hx_target_click` |
| `EChartUpdate(chart_id, options, merge)` | Update an existing chart instance |
| `JSFunc(js_string)` | Mark a string as raw JavaScript (for formatters, callbacks, etc.) |
| `preview_echart(echart, height)` | Preview a chart in a notebook via iframe |
