@pond-ts/charts 0.55.0 → 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.
- package/CHANGELOG.md +288 -1
- package/dist/AreaChart.d.ts +18 -0
- package/dist/AreaChart.js +24 -1
- package/dist/BarChart.d.ts +84 -9
- package/dist/BarChart.js +113 -12
- package/dist/ChartContainer.d.ts +44 -1
- package/dist/ChartContainer.js +18 -2
- package/dist/ChartRow.js +57 -6
- package/dist/Layers.js +14 -1
- package/dist/YAxis.d.ts +56 -1
- package/dist/YAxis.js +28 -3
- package/dist/annotations.d.ts +74 -0
- package/dist/annotations.js +97 -7
- package/dist/area.js +46 -15
- package/dist/band.js +13 -0
- package/dist/bars.d.ts +139 -4
- package/dist/bars.js +300 -33
- package/dist/context.d.ts +30 -5
- package/dist/dev.d.ts +2 -0
- package/dist/dev.js +2 -0
- package/dist/domain.d.ts +54 -1
- package/dist/domain.js +195 -2
- package/dist/format.d.ts +20 -0
- package/dist/format.js +23 -10
- package/dist/gaps.d.ts +33 -0
- package/dist/gaps.js +49 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -2
- package/dist/line.js +10 -1
- package/dist/theme.d.ts +73 -6
- package/dist/theme.js +5 -0
- package/dist/viewport.d.ts +9 -1
- package/dist/viewport.js +40 -4
- package/dist/yticks.d.ts +44 -0
- package/dist/yticks.js +55 -0
- package/package.json +3 -3
package/dist/viewport.js
CHANGED
|
@@ -36,28 +36,64 @@ export function clampToBounds(range, bounds) {
|
|
|
36
36
|
return [hi - span, hi];
|
|
37
37
|
return [range[0], range[1]];
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Snap a computed view range to **whole milliseconds** — the last step of every
|
|
41
|
+
* gesture that derives a range from pixels.
|
|
42
|
+
*
|
|
43
|
+
* A wheel-zoom or drag-pan turns a pixel position into a time via
|
|
44
|
+
* `xScale.invert()`, so the result is fractional *by construction*: an ordinary
|
|
45
|
+
* scroll produces `1.7e12 + 0.37`. The epoch millisecond is this model's atomic
|
|
46
|
+
* unit — a sub-millisecond view range is not a finer view, it is a number with
|
|
47
|
+
* no meaning — and downstream consumers are entitled to assume it. One of them
|
|
48
|
+
* did: `Temporal.Instant` refuses a non-integer epoch ms outright, so a
|
|
49
|
+
* `cursorSequence` over a calendar grain threw on a plain scroll and unmounted
|
|
50
|
+
* the page. Core now floors the instant, which fixes that symptom; rounding
|
|
51
|
+
* here closes the class, because nothing downstream ever sees the fraction.
|
|
52
|
+
*
|
|
53
|
+
* **Never collapses a positive span.** `[10.4, 10.6]` would otherwise round to
|
|
54
|
+
* `[10, 10]` — a zero-width view, which is a division by zero in every scale
|
|
55
|
+
* built from it. A span that survives rounding keeps its rounded width; one
|
|
56
|
+
* that doesn't is opened to the 1 ms floor. A range that arrives degenerate
|
|
57
|
+
* (`hi <= lo`) is passed through rounded, since widening it would invent a view
|
|
58
|
+
* the caller didn't ask for.
|
|
59
|
+
*/
|
|
60
|
+
function roundRange(lo, hi) {
|
|
61
|
+
const a = Math.round(lo);
|
|
62
|
+
const b = Math.round(hi);
|
|
63
|
+
// `Math.round` is monotonic, so `b < a` is impossible for `hi >= lo`; the only
|
|
64
|
+
// way a positive span collapses is both ends landing on the same integer.
|
|
65
|
+
return b === a && hi > lo ? [a, a + 1] : [a, b];
|
|
66
|
+
}
|
|
39
67
|
/**
|
|
40
68
|
* Shift a range by `dt` ms (drag-pan). The caller signs `dt` from the gesture —
|
|
41
|
-
* dragging the plot right reveals earlier data, i.e. a negative `dt`.
|
|
69
|
+
* dragging the plot right reveals earlier data, i.e. a negative `dt`. The result
|
|
70
|
+
* is snapped to whole milliseconds ({@link roundRange}) — `dt` comes from a pixel
|
|
71
|
+
* delta through `xScale.invert()`, so it is fractional by construction.
|
|
42
72
|
*/
|
|
43
73
|
export function panRange(range, dt) {
|
|
44
|
-
return
|
|
74
|
+
return roundRange(range[0] + dt, range[1] + dt);
|
|
45
75
|
}
|
|
46
76
|
/**
|
|
47
77
|
* Zoom `range` around `pivot` (ms) by `factor` — `< 1` zooms in, `> 1` out, with
|
|
48
78
|
* the pivot held fixed (the time under the cursor stays put). Clamped so the
|
|
49
79
|
* duration never drops below `minDuration` (the zoom-in floor); at the floor the
|
|
50
80
|
* pivot keeps its fractional position in the window.
|
|
81
|
+
*
|
|
82
|
+
* The result is snapped to whole milliseconds ({@link roundRange}). `minDuration`
|
|
83
|
+
* is applied **before** the snap, so the floor is honoured in the units the
|
|
84
|
+
* caller expressed it in; a `minDuration` below 1 ms cannot be represented and
|
|
85
|
+
* lands on the 1 ms floor the snap guarantees, which is the finest view this
|
|
86
|
+
* model has.
|
|
51
87
|
*/
|
|
52
88
|
export function zoomRange(range, pivot, factor, minDuration = 1) {
|
|
53
89
|
const lo = pivot - (pivot - range[0]) * factor;
|
|
54
90
|
const hi = pivot + (range[1] - pivot) * factor;
|
|
55
91
|
if (hi - lo >= minDuration)
|
|
56
|
-
return
|
|
92
|
+
return roundRange(lo, hi);
|
|
57
93
|
// Floor reached: hold the pivot's fractional position, set span = minDuration.
|
|
58
94
|
const span = range[1] - range[0];
|
|
59
95
|
const frac = span > 0 ? (pivot - range[0]) / span : 0.5;
|
|
60
|
-
return
|
|
96
|
+
return roundRange(pivot - minDuration * frac, pivot + minDuration * (1 - frac));
|
|
61
97
|
}
|
|
62
98
|
/**
|
|
63
99
|
* Pan a range on a **trading-time** axis: shift both endpoints by the same
|
package/dist/yticks.d.ts
CHANGED
|
@@ -17,4 +17,48 @@
|
|
|
17
17
|
* nice 1-2-5 values near it, so a larger count on a tall row is exactly right.
|
|
18
18
|
*/
|
|
19
19
|
export declare function resolveYTickCount(height: number, explicit?: number | undefined): number;
|
|
20
|
+
/** The slice of a scale {@link yTickValues} reads. */
|
|
21
|
+
interface TickableScale {
|
|
22
|
+
ticks(count?: number): number[];
|
|
23
|
+
domain(): number[];
|
|
24
|
+
/** Present on d3's `scaleLog` and on no other continuous scale. */
|
|
25
|
+
base?: () => number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The y tick **values** a `<YAxis>`'s labels and the row's gridlines draw —
|
|
29
|
+
* the one list, so a label and its gridline stay on the same instants.
|
|
30
|
+
*
|
|
31
|
+
* On a linear scale this is just `scale.ticks(count)`, whose 1-2-5 selection
|
|
32
|
+
* treats the count as a target. **On a log scale it cannot be**, because d3's
|
|
33
|
+
* `scaleLog.ticks(count)` is not a target at all — it is nearly a step
|
|
34
|
+
* function, and the jump is catastrophic. Measured against a real seven-decade
|
|
35
|
+
* domain (ESnet's traffic history, 1.9e10 → 2.6e17 bytes):
|
|
36
|
+
*
|
|
37
|
+
* | `count` | ticks returned |
|
|
38
|
+
* | ------- | -------------- |
|
|
39
|
+
* | 4 | 3 (every *other* decade — 1e12, 1e14, 1e16) |
|
|
40
|
+
* | 6 | 7 (every decade — the one right answer) |
|
|
41
|
+
* | 8 | **64** (every 2,3,…9 × decade) |
|
|
42
|
+
*
|
|
43
|
+
* Since the count is height-derived (`height / 48`), that means a 260px row
|
|
44
|
+
* silently labels 3 of 7 decades and a 400px row draws 64 gridlines and 64
|
|
45
|
+
* labels — a 40px resize flipping between them. Neither is a rendering nicety;
|
|
46
|
+
* both are unreadable.
|
|
47
|
+
*
|
|
48
|
+
* So for a log scale we pick the decades ourselves: every `k`th power of ten,
|
|
49
|
+
* with `k` the smallest step whose tick count fits the budget. That is what a
|
|
50
|
+
* log plot is conventionally gridded on, it degrades predictably as the row
|
|
51
|
+
* shrinks, and it never explodes.
|
|
52
|
+
*
|
|
53
|
+
* Below two decades of span there aren't enough powers of ten to grid with, and
|
|
54
|
+
* d3's within-decade selection (2,3,…9 × 10ⁿ) is the right answer and is well
|
|
55
|
+
* behaved — so that case defers to the scale.
|
|
56
|
+
*
|
|
57
|
+
* Log detection is structural: `base()` exists on `scaleLog` and on no other
|
|
58
|
+
* continuous scale. The alternative is threading the axis kind down to the
|
|
59
|
+
* gridline site, which has no `AxisSpec` in scope — the same localized-shape
|
|
60
|
+
* approach `resolveBarBaseline` takes to read `.domain()`.
|
|
61
|
+
*/
|
|
62
|
+
export declare function yTickValues(scale: TickableScale, count: number): number[];
|
|
63
|
+
export {};
|
|
20
64
|
//# sourceMappingURL=yticks.d.ts.map
|
package/dist/yticks.js
CHANGED
|
@@ -25,4 +25,59 @@ export function resolveYTickCount(height, explicit) {
|
|
|
25
25
|
return Math.max(1, Math.floor(explicit));
|
|
26
26
|
return Math.max(2, Math.floor(height / Y_TICK_PX));
|
|
27
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* The y tick **values** a `<YAxis>`'s labels and the row's gridlines draw —
|
|
30
|
+
* the one list, so a label and its gridline stay on the same instants.
|
|
31
|
+
*
|
|
32
|
+
* On a linear scale this is just `scale.ticks(count)`, whose 1-2-5 selection
|
|
33
|
+
* treats the count as a target. **On a log scale it cannot be**, because d3's
|
|
34
|
+
* `scaleLog.ticks(count)` is not a target at all — it is nearly a step
|
|
35
|
+
* function, and the jump is catastrophic. Measured against a real seven-decade
|
|
36
|
+
* domain (ESnet's traffic history, 1.9e10 → 2.6e17 bytes):
|
|
37
|
+
*
|
|
38
|
+
* | `count` | ticks returned |
|
|
39
|
+
* | ------- | -------------- |
|
|
40
|
+
* | 4 | 3 (every *other* decade — 1e12, 1e14, 1e16) |
|
|
41
|
+
* | 6 | 7 (every decade — the one right answer) |
|
|
42
|
+
* | 8 | **64** (every 2,3,…9 × decade) |
|
|
43
|
+
*
|
|
44
|
+
* Since the count is height-derived (`height / 48`), that means a 260px row
|
|
45
|
+
* silently labels 3 of 7 decades and a 400px row draws 64 gridlines and 64
|
|
46
|
+
* labels — a 40px resize flipping between them. Neither is a rendering nicety;
|
|
47
|
+
* both are unreadable.
|
|
48
|
+
*
|
|
49
|
+
* So for a log scale we pick the decades ourselves: every `k`th power of ten,
|
|
50
|
+
* with `k` the smallest step whose tick count fits the budget. That is what a
|
|
51
|
+
* log plot is conventionally gridded on, it degrades predictably as the row
|
|
52
|
+
* shrinks, and it never explodes.
|
|
53
|
+
*
|
|
54
|
+
* Below two decades of span there aren't enough powers of ten to grid with, and
|
|
55
|
+
* d3's within-decade selection (2,3,…9 × 10ⁿ) is the right answer and is well
|
|
56
|
+
* behaved — so that case defers to the scale.
|
|
57
|
+
*
|
|
58
|
+
* Log detection is structural: `base()` exists on `scaleLog` and on no other
|
|
59
|
+
* continuous scale. The alternative is threading the axis kind down to the
|
|
60
|
+
* gridline site, which has no `AxisSpec` in scope — the same localized-shape
|
|
61
|
+
* approach `resolveBarBaseline` takes to read `.domain()`.
|
|
62
|
+
*/
|
|
63
|
+
export function yTickValues(scale, count) {
|
|
64
|
+
if (typeof scale.base !== 'function')
|
|
65
|
+
return scale.ticks(count);
|
|
66
|
+
const domain = scale.domain();
|
|
67
|
+
const lo = Math.min(domain[0], domain[domain.length - 1]);
|
|
68
|
+
const hi = Math.max(domain[0], domain[domain.length - 1]);
|
|
69
|
+
if (!(lo > 0) || !(hi > lo))
|
|
70
|
+
return scale.ticks(count);
|
|
71
|
+
const first = Math.ceil(Math.log10(lo));
|
|
72
|
+
const last = Math.floor(Math.log10(hi));
|
|
73
|
+
const decades = last - first + 1;
|
|
74
|
+
if (decades < 2)
|
|
75
|
+
return scale.ticks(count);
|
|
76
|
+
const budget = Math.max(2, count);
|
|
77
|
+
const step = Math.max(1, Math.ceil(decades / budget));
|
|
78
|
+
const out = [];
|
|
79
|
+
for (let e = first; e <= last; e += step)
|
|
80
|
+
out.push(10 ** e);
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
28
83
|
//# sourceMappingURL=yticks.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pond-ts/charts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.57.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Canvas-rendered, streaming-first time-series charts for pond-ts",
|
|
6
6
|
"license": "MIT",
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"perf": "PERF_BENCH=1 playwright test perf.spec.ts --workers=1"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
|
-
"@pond-ts/react": "^0.
|
|
42
|
-
"pond-ts": "^0.
|
|
41
|
+
"@pond-ts/react": "^0.57.0",
|
|
42
|
+
"pond-ts": "^0.57.0",
|
|
43
43
|
"react": "^18.0.0 || ^19.0.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|