A discrete box-and-whisker draw layer — the bar-chart analog of the variance
band. Reads pre-computed quantile columns of series (typically a
rolling/aggregate percentile pass — the chart does not compute them)
into a BoxSeries and draws one box per key: the q1→q3 body, the median
line, and whiskers out to lower/upper. Registers itself into the enclosing
Layers; renders nothing to the DOM — the row draws it.
- Any axis. A
TimeSeriesplots on time, aValueSeries(series.byValue('strike')orValueSeries.fromColumns) on its value axis — a vol smile's per-strike IV. The box width is the interval key's[begin, end)or, for a point key (aValueSeries, or a point-keyedTimeSeries), neighbour spacing — so it never collapses to the 1px floor. - Range-only.
q1/q3(the body) andmedian(the centre line) are optional: omitq1+q3for a whisker-onlylower→uppersegment — a bid→ask IV mark. Gap-aware: a key missing any present quantile draws nothing. offsetnudges the whole layer in pixel space, for pairing same-key marks (call/put at one strike) side by side.
There's no baseline — a box is a spread, not a bar to a floor; the y-domain auto-fits the whisker reach (lower→upper).
<Layers>
<BoxPlot series={q} lower="p5" q1="p25" median="p50" q3="p75" upper="p95"
as="latency" gap={6} />
// range-only bid→ask on a value axis (a vol smile):
<BoxPlot series={smile} lower="bid" upper="ask" />
</Layers>
Props
Name of the numeric column for the lower whisker end (e.g. p5 / min).
Required — with upper it's the whisker reach.
seriesTimeSeriesTimeSeriesclasspond-tsAn immutable, schema-typed, ordered collection of events — the batch
layer's core primitive. A series is constructed whole from complete data
and never mutated: every transform (filter, align, rollup, …)
returns a new TimeSeries, so the full analytical surface can sort,
scan, or index freely. Example:
new TimeSeries({ name, schema, rows }).
<S> | ValueSeriesValueSeriesclasspond-tsA value-keyed series — the closed value-axis counterpart of
TimeSeries. Its key is a monotonic non-time axis (distance, cumulative
work, …). Two doors in: project a TimeSeries onto one of its monotonic
columns (TimeSeries.byValue(axis) — a track re-keyed by cumulative
distance), or construct directly from columnar arrays
(`ValueSeries.fromC…
<VS>requiredThe source series. A TimeSeries plots against the time axis; a ValueSeries
(series.byValue('strike'), or ValueSeries.fromColumns for natively
value-keyed data — a per-strike IV distribution) against its value axis — the
container infers which from the data, no axis-type prop (mirrors <LineChart>
/ <ScatterChart>). The box x-span is the key's [begin, end) for an
interval-keyed TimeSeries, else synthesized from neighbour spacing (a
point-keyed TimeSeries, or a ValueSeries) so the box keeps real width.
Name of the numeric column for the upper whisker end (e.g. p95 / max).
Required — with lower it's the whisker reach.
asstringThe box series' semantic identifier — what the spread is (e.g. latency).
The theme maps it to a BoxStyle (theme.box[as] ?? theme.box.default
— box fill/outline, median, whisker). Omitted ⇒ the default box style;
there's no per-component colour/style override (restyle via the theme, the
single styling channel).
axisstringWhich <YAxis> (by its id) this box scales against — the scale, where
as picks the style. Omitted ⇒ the row's default axis.
capWidthnumberWhisker end-cap total width in pixels (the top/bottom bars of the T).
Omitted ⇒ half the box width (responsive — scales with the slot). Set a
small fixed value (e.g. 6) to keep the caps narrow so two offset-paired
marks (call/put at one strike) don't overlap their T-bars — the caps no longer
grow with the wide value-axis slot. Clamped to the box width. Only affects the
'whisker' shape ('solid'/'none' have no caps).
gapnumberTotal horizontal inset between adjacent boxes in px (half each side), so they
breathe — see barSpanPx. Omitted ⇒ 0 (boxes fill their interval span
edge-to-edge). A box narrower than 1px after the inset collapses to a 1px
mark centred in its slot, so a thin bucket stays visible.
medianstringName of the numeric column for the median line (e.g. p50). Optional —
omit for no centre line (independent of the box body).
offsetnumberA pixel shift applied to every box's x — zoom-stable (unlike a data-space
nudge). Default 0. For pairing marks that share a key side by side: e.g.
a call and a put box at the same strike, offset={-4} / offset={+4} (the
react-timeseries-charts side-by-side-bars precedent). Pairs with
<ScatterChart offset>.
Only the draw is shifted. The box's readouts stay in un-shifted data
space — the off-chart hover finds a box by span containment (xScale.invert),
and the in-chart flag staff anchors at the box's data centre — so both can
sit up to offset px from the pixel-shifted box. Keep the offset small (a
pairing nudge, not a layout tool) and it's imperceptible. (<ScatterChart offset> has no such gap — its hit-test is pixel-space and shifts too.)
q1stringName of the numeric column for the box bottom — first quartile (e.g. p25).
Optional: omit q1 and q3 together for a range-only box — a
whisker-only lower→upper segment, no body (a bid→ask IV mark). Giving just
one of q1/q3 throws.
q3stringName of the numeric column for the box top — third quartile (e.g. p75).
Optional — omit with q1 for a range-only box (see q1).
shapeBoxShapeHow each box renders its spread — 'whisker' (default; thin stems + caps),
'solid' (the candlestick look: a light outer bar over the full range with a
darker inner q1→q3 box, no stems), or 'none' (the q1→q3 box only, no spread
marks). See BoxShape. On a range-only box (no q1/q3),
'whisker' is one full lower→upper stem and 'solid' the outer bar; 'none'
would draw nothing (no body + no spread), so use 'whisker'/'solid' there.
showMedianbooleanDraw the median (centre) line across each box. Default true, but a no-op
when the median column is omitted (nothing to draw). The median prop
names the column; this toggles the line.