@pond-ts/react 0.56.2 → 0.57.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/CHANGELOG.md +149 -1
  2. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -8,7 +8,8 @@ The `@pond-ts` packages — `pond-ts`, `@pond-ts/react`, `@pond-ts/charts`,
8
8
  under a single `v*` tag, so this file covers them all. Pre-1.0: minor bumps may
9
9
  include new features and type-level changes; patch bumps are strictly additive.
10
10
 
11
- [Unreleased]: https://github.com/pond-ts/pond/compare/v0.56.2...HEAD
11
+ [Unreleased]: https://github.com/pond-ts/pond/compare/v0.57.0...HEAD
12
+ [0.57.0]: https://github.com/pond-ts/pond/compare/v0.56.2...v0.57.0
12
13
  [0.56.2]: https://github.com/pond-ts/pond/compare/v0.56.1...v0.56.2
13
14
  [0.56.1]: https://github.com/pond-ts/pond/compare/v0.56.0...v0.56.1
14
15
  [0.56.0]: https://github.com/pond-ts/pond/compare/v0.55.0...v0.56.0
@@ -59,6 +60,153 @@ include new features and type-level changes; patch bumps are strictly additive.
59
60
 
60
61
  ## [Unreleased]
61
62
 
63
+ ## [0.57.0] — 2026-08-07
64
+
65
+ ### Added
66
+
67
+ - **charts: `<Zone>` — a shaded y-span annotation.** The fourth annotation
68
+ mark, and the value-axis counterpart of `<Region>`: a band between two y
69
+ values, spanning the full plot width. The mark for a **classification of the
70
+ value axis** — US EPA AQI categories, heart-rate / power zones, SLO bands,
71
+ control-chart spec limits — where a reading only means something read against
72
+ a scale.
73
+
74
+ ```tsx
75
+ {
76
+ AQI_CATEGORIES.map((c) => (
77
+ <Zone key={c.role} from={c.from} to={c.to} axis="aqi" role={c.role} />
78
+ ));
79
+ }
80
+ ```
81
+
82
+ Colour comes from `theme.annotation.roles[role]`, so a zone _set_ styles as
83
+ one palette in the theme rather than N colours at the call site. Bounds are
84
+ order-free, clamped to the plot (a band past the axis domain is cut, one
85
+ fully outside culls), and accept `±Infinity` for genuinely open-ended bands
86
+ (`to={Infinity}` — AQI's "Hazardous", a `ZoneTime.openEnded` zone), so a
87
+ whole category table can be rendered and the axis decides what shows.
88
+
89
+ Three defaults deliberately **invert** the rest of the annotation family,
90
+ because a zone spans the full width and a set tiles the row:
91
+ `selectable={false}` (else every mousemove lights a band and its hit area
92
+ eats the plot's clicks), `edges={false}` (contiguous sets share every
93
+ interior boundary — edges-on draws each twice), and no auto-label (the bounds
94
+ are already legible on the y axis; the useful label is a name). `<Zone>` has
95
+ no `onChange` — drag-to-edit zones await a consumer.
96
+
97
+ Guide: [From a CSV to a banded chart](https://pond-ts.org/docs/how-to-guides/air-quality-bands).
98
+
99
+ - **charts: `annotation.dash` — an optional dash pattern for the annotation
100
+ register**, per-register or per-role (`{ color, fillOpacity?, dash? }`), same
101
+ shape as `LineStyle.dash`. Applies to marker / baseline lines and region /
102
+ zone boundaries; fills are never dashed. A dashed reference line reads as
103
+ _placed_ rather than _measured_ — the job the annotation register exists to
104
+ do, and one colour alone can't always carry.
105
+
106
+ - **charts: threshold-banded bars** — `<BarChart thresholds={[1, 2]}>` colours
107
+ one bar **along its length** against a ladder (neutral → warning → alarm), so
108
+ a long bar shows how far through the ladder it travelled rather than only
109
+ which band it ended in. Band fills come from the new
110
+ **`BarStyle.bands`** on the resolved theme role, overridable per chart with
111
+ **`<BarChart bandColors>`** — breakpoints are data, colour stays in the theme,
112
+ the same split `colors` already applies to a stack's group fills.
113
+
114
+ The shape was previously expressible as N `<BarChart>` layers drawn
115
+ outermost-first, each clipped to a band, compositing the gradient by
116
+ overpainting. That produces the same pixels and loses what matters: N layers
117
+ means N hit targets, N `SelectInfo.mark` identities and N legend rows for
118
+ something the reader sees as one bar. Banding is **draw-only** — the hit rect
119
+ is untouched, so a banded bar stays one bar. It also measures **27–49%
120
+ cheaper** than the layered workaround at 8–400 categories
121
+ (`scripts/perf-bandbar.mjs`, which interleaves the arms and prints the
122
+ ratios), and costs nothing when unused.
123
+
124
+ Applies to any single-value bar — `series`, `bins`, `categories`, both
125
+ orientations. Negatives band symmetrically on the magnitude, so a ± diverging
126
+ scale needs no negative breakpoints. Ignored (with a dev warning) on a
127
+ multi-group stack, and yields to `binColors` when both are set. Suppresses
128
+ envelope decimation for the same reason `binColors` does.
129
+
130
+ - **charts: `<YAxis hide>`** — keep the scale, draw no gutter, reserve no
131
+ width. A `<YAxis>` does two jobs — it _holds the scale_ (`min`/`max`/`scale`/
132
+ `pad`) and it _renders a gutter_ — and there was no way to ask for the first
133
+ without the second. A caller could express "auto domain, no gutter" (omit the
134
+ axis) or "explicit domain, with a gutter", but not the pairing a fixed-domain
135
+ chart needs. Omitting the axis is not equivalent: the row supplies an implicit
136
+ auto-domain axis, which is exactly what must not be given up. `width={0}`
137
+ isn't either — the labels still draw, over the plot.
138
+
139
+ Gridlines are unaffected: they belong to the plot, not the gutter, and
140
+ `<ChartContainer grid>` already governs them.
141
+
142
+ - **charts: `<ChartContainer maxBandWidth>` + `bandAlign`** — cap the **slot
143
+ pitch** on a category x axis and place the resulting block
144
+ (`'start'` (default) / `'center'` / `'end'`). A band scale otherwise spreads
145
+ its categories across the full plot width, so three categories in a 900px
146
+ panel become three 300px bars and thirty become thirty 30px ones — the same
147
+ chart in the same panel reading as two different charts depending on how many
148
+ categories the data returned. Fine for a fixed domain; wrong for a **live**
149
+ one, where bar width becomes a variable that moves on its own and a reader
150
+ can't compare the chart to itself a minute ago.
151
+
152
+ `maxBandWidth` caps the **slot**; `<BarChart gap>` still insets the bar
153
+ within it — one knob for pitch, one for ink. Omitting `maxBandWidth` is the
154
+ previous fill behaviour exactly, and a cap too loose to bind degrades back to
155
+ it rather than clipping. There is deliberately no `bandAlign: 'fill'`: "fill"
156
+ is what omitting the cap means, and a `fill` alongside a pitch cap would be a
157
+ contradiction rather than a choice.
158
+
159
+ **Vertical / x-axis categories only** — a `orientation="horizontal"`
160
+ categorical chart puts its categories on the y axis as unit slots, a
161
+ different mechanism this does not cap.
162
+
163
+ - **charts: themed emphasis on the category path** — `BarStyle.hover` /
164
+ `.highlight` now apply to `categories` and horizontal bars, which routed
165
+ through the transposed stacked draw path and read neither. New
166
+ `BarStyle.selectedOutline` (the selected bar's stroke, where the default is
167
+ its own fill) and `BarStyle.emphasisOpacity` (the alpha a live bar pops to,
168
+ previously hard-coded `1`) make the emphasis tunable rather than fixed.
169
+
170
+ The _behaviour_ was defensible; the problem was that the theme accepted
171
+ values it would not use. `bar.hover` / `.highlight` were typed, settable and
172
+ documented as the emphasis channel, and silently did nothing on the most
173
+ common categorical chart, so a theme author set them, saw no change, and
174
+ could not tell whether they were wrong about the colour or the mechanism.
175
+ The one genuine exclusion stays and is now the only one: a `binColors` bar
176
+ keeps its own colour under hover/selection, because swapping a
177
+ zone-coloured or direction-coloured bar to a single highlight hue erases
178
+ what the colour encodes.
179
+
180
+ ### Fixed
181
+
182
+ - **charts: a negative segment in a multi-group stack is no longer silently
183
+ dropped** ([PND-SIGNSTACK]). Positives stack **up** from the baseline and
184
+ negatives stack **down** from it — two running totals per bin — so the
185
+ **signed stacked histogram** (net flow by category, inflow/outflow, buy/sell
186
+ pressure by venue) renders correctly. `stackValueExtent` grew the matching
187
+ negative half; both had to move together, since an extent stopping at `0`
188
+ would clip the segments the draw path now emits.
189
+
190
+ **This is a visible behaviour change** for any existing chart feeding
191
+ negative values into a multi-group stack — but such a chart was previously
192
+ rendering _wrongly_: the dropped segments did not clamp, warn or throw, every
193
+ remaining segment stacked up as though they had never been in the data, and a
194
+ mixed-sign series came out as a confident, wrong, all-positive chart. An
195
+ all-positive stack is bit-identical to before. Splitting into two layers was
196
+ not a workaround either — the negative layer was still `G > 1`, so it was
197
+ dropped too.
198
+
199
+ ### Changed
200
+
201
+ - **charts (docs): `<BarChart bins>` now states that it selects a _value_
202
+ axis** ([PND-TICKUNIT]), so a time-bucketed histogram fed through `bins` gets
203
+ the decimal 1-2-5 tick ladder rather than the duration ladder a clock
204
+ subdivides by — labelling e.g. 11:40 and 13:20 at a ~100-minute step. The
205
+ natural reading ("I have pre-binned buckets, so I'll pass `bins`") is exactly
206
+ what forecloses the time axis, and nothing at the call site said so. The prop
207
+ docs now point a time-keyed caller at `<BarChart series columns>`, where the
208
+ clock ticks are native. No behaviour change.
209
+
62
210
  ## [0.56.2] — 2026-08-05
63
211
 
64
212
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pond-ts/react",
3
- "version": "0.56.2",
3
+ "version": "0.57.0",
4
4
  "description": "React hooks for pond-ts live time series",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -33,7 +33,7 @@
33
33
  "test:runtime": "vitest run"
34
34
  },
35
35
  "peerDependencies": {
36
- "pond-ts": "^0.56.2",
36
+ "pond-ts": "^0.57.0",
37
37
  "react": "^18.0.0 || ^19.0.0"
38
38
  },
39
39
  "devDependencies": {