feat(editor): chart behaviour over time with uPlot
Draw the simulation's Stock trajectories with uPlot (ADR-0005): a real time axis, hover-to-read values, and a playhead-marker hook the canvas animation drives. SimChart owns the imperative uPlot lifecycle behind a small prop API.
This commit is contained in:
55
docs/adr/0005-uplot-behaviour-chart.md
Normal file
55
docs/adr/0005-uplot-behaviour-chart.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# uPlot draws the behaviour-over-time chart
|
||||
|
||||
_Part of [meadows](../../README.md) · see [DESIGN.md](../../DESIGN.md)._
|
||||
|
||||
The Results panel (ADR-0004's phase 2) traces each Stock's value over time. It
|
||||
began as a **hand-built SVG** — a deliberate lean-substrate choice, no charting
|
||||
dependency. But a polyline is all that substrate could draw: no time axis, no
|
||||
gridlines, and a legend that could only show a Stock's _final_ value, never the
|
||||
value under the cursor. For a tool whose whole point is reading _behaviour over
|
||||
time_, "what is this Stock at t = 40?" is the question you most want to answer by
|
||||
pointing at the curve.
|
||||
|
||||
## Decision
|
||||
|
||||
Adopt **uPlot** (`uplot`, ~15 KB gzip) — a small, fast canvas time-series
|
||||
library — for the behaviour-over-time chart. It earns the dependency with the
|
||||
three things the SVG could not give: a real **time axis** with ticks and
|
||||
gridlines, and a live legend that **reads each Stock's value at the hovered
|
||||
time**.
|
||||
|
||||
uPlot is imperative and canvas-based, so it is wrapped in **`SimChart.vue`**,
|
||||
which owns its lifecycle (build on mount, `setData` on recompute, rebuild on a
|
||||
track-set change, `ResizeObserver` for width, destroy on unmount). `ResultsPanel`
|
||||
stays declarative: it assembles plain aligned data (Stocks only — the system's
|
||||
memory, per ADR-0004) and hands it down.
|
||||
|
||||
## Considered Options
|
||||
|
||||
- **Keep the hand-built SVG** (zero deps) — rejected: re-implementing axes,
|
||||
tick placement, a hover cursor, and hit-testing _is_ rebuilding a charting
|
||||
library, badly. The lean-substrate note was a code comment, not an ADR; nothing
|
||||
prior is contradicted by spending one dependency where it clearly pays.
|
||||
- **`@gouvfr/dsfr-chart`** (the original prompt) — rejected: it requires the
|
||||
entire DSFR design system (CSS + JS API) and embeds its _own_ Vue and Chart.js,
|
||||
so we would ship Vue twice and a second design language clashing with DaisyUI.
|
||||
It is built for French-government dashboards (region maps, gauges), not generic
|
||||
time series.
|
||||
- **Chart.js / vue-chartjs** — viable and same engine class, but heavier; uPlot
|
||||
is lighter and faster for many series and live-updating data, which is exactly
|
||||
the simulation's shape.
|
||||
- **uPlot (chosen)** — lightest credible option that gives the axis + hover.
|
||||
|
||||
## Consequences
|
||||
|
||||
- This is the project's **first runtime charting dependency**. The price, as with
|
||||
Vue Flow (ADR-0002), is a thin wrapper layer: `SimChart.vue` translates between
|
||||
reactive props and uPlot's imperative API.
|
||||
- The x scale runs with **`time: false`** — x is _simulation_ time (`start…stop`),
|
||||
not wall-clock, which uPlot would otherwise format as calendar dates.
|
||||
- Track colours are read from the **DaisyUI theme variables**. The app ships a
|
||||
single `light` theme, so they are resolved once at build time; a future dark
|
||||
theme would need the plot re-initialised on theme change.
|
||||
- Swapping charting libraries later means rewriting only `SimChart.vue` —
|
||||
`ResultsPanel` passes plain `[times, ...tracks]` aligned data and knows nothing
|
||||
of uPlot.
|
||||
Reference in New Issue
Block a user