# `LineaUI.Components.Chart`
[🔗](https://github.com/devaction-labs/linea_ui/blob/v0.1.1/lib/linea_ui/components/chart.ex#L1)

An ApexCharts chart with LineOne's skin (axis labels, grid lines, legend, tooltips, light
and dark).

    <.chart
      id="sales"
      options={%{
        chart: %{type: "area", height: 350, toolbar: %{show: false}},
        colors: ["#34d399", "#ffba1a"],
        series: [%{name: "Series1", data: [31, 40, 28, 51]}],
        xaxis: %{categories: ["Jan", "Feb", "Mar", "Apr"]}
      }}
    />

The app provides the library: install `apexcharts` and set `window.ApexCharts` before the
LiveView connects.

    import ApexCharts from "apexcharts"
    window.ApexCharts = ApexCharts

`options` is any ApexCharts options object, as a map or a JSON string. When it changes on
the server the chart is updated in place. To stream data without re-sending the whole
options, push an event:

    push_event(socket, "linea:chart", %{id: "sales", series: [%{data: [1, 2, 3]}]})

`series` and/or `options` in the payload are applied with `updateSeries`/`updateOptions`.

JSON has no functions, so formatters are named and resolved in the browser. `formatters` maps
an options path to a built-in formatter, and a bare `formatter: "name"` string inside the
options works too:

    <.chart
      id="sales"
      formatters={%{"yaxis.labels.formatter" => "thousands", "tooltip.y.formatter" => "dollars"}}
      options={%{series: [...], plotOptions: %{radialBar: %{dataLabels: %{total: %{formatter: "total"}}}}}}
    />

| name | `value` becomes |
|---|---|
| `thousands` | `2500` -> `2.5k` (value / 1000, no rounding) |
| `thousands_round` | `2500` -> `3k` |
| `thousands_fixed` | `2500` -> `2.50k`, values under 1000 unchanged |
| `kilo` | `1499` -> `1k`, values under 1000 unchanged |
| `dollars` | `5` -> `$5` |
| `dollars_thousands` | `5000` -> `$5k` |
| `percent` | `12.5` -> `12.5%` |
| `percent_round` | `12.5` -> `13%` |
| `round` | `2.6` -> `3` |
| `hours` | `3` -> `3h` |
| `minutes` | `45` -> `45 min` |
| `none` | hides the label (empty text) |
| `total` | for gauges: the sum of the series (takes ApexCharts' `w`, not a value) |

Names not in the table are left as they are. For anything else, before rendering, the
chart element dispatches a bubbling `linea:chart:options` event whose `detail.options` may
be changed.

    window.addEventListener("linea:chart:options", ({target, detail}) => {
      target.id === "sales" && (detail.options.yaxis = {labels: {formatter: (v) => "$" + v}})
    })

# `chart`

Renders a chart. The `LineaUI.Chart` hook draws it into an inner element LiveView leaves
alone, and redraws it in place (`updateOptions`) whenever the `options` assign changes.

    <.chart
      id="revenue"
      class="max-w-xl"
      options={%{
        chart: %{type: "bar", height: 350, toolbar: %{show: false}},
        colors: ["#ff9800", "#6366f1"],
        plotOptions: %{bar: %{horizontal: true, borderRadius: 5}},
        series: [%{name: "Net Profit", data: @profit}, %{name: "Revenue", data: @revenue}],
        xaxis: %{categories: @months}
      }}
    />

Server push (no re-render), from `handle_event/3` or `handle_info/2`; payload
`%{id: chart_id, series: [...]}` and/or `options: %{...}`, `id` with or without `#`:

    push_event(socket, "linea:chart", %{id: "revenue", series: [%{data: [1, 2, 3]}]})

Browser event: `linea:chart:options` (bubbling, on the chart element) fires before each
draw with `detail.options`, the place to add JavaScript formatters. `data-state` on the
element is `ready`, or `waiting` until `window.ApexCharts` exists.

## Attributes

* `id` (`:string`) (required) - DOM id; also what `push_event` payloads target.
* `options` (`:any`) (required) - ApexCharts options, a map (encoded to JSON) or an already encoded JSON string.
* `formatters` (`:map`) - Options path to built-in formatter name, e.g. `%{"yaxis.labels.formatter" => "thousands"}`; see the table in the module doc. Applied before every draw. Defaults to `%{}`.
* `class` (`:any`) - Defaults to `nil`.
* Global attributes are accepted.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
