@wick-charts/vue 0.4.1 → 0.5.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/README.md +5 -3
- package/dist/BarSeries.vue.js +44 -0
- package/dist/BarSeries.vue2.js +4 -0
- package/dist/CandlestickSeries.vue.js +45 -0
- package/dist/CandlestickSeries.vue2.js +4 -0
- package/dist/ChartContainer.vue.js +168 -0
- package/dist/ChartContainer.vue2.js +4 -0
- package/dist/LineSeries.vue.js +44 -0
- package/dist/LineSeries.vue2.js +4 -0
- package/dist/PieSeries.vue.js +34 -0
- package/dist/PieSeries.vue2.js +4 -0
- package/dist/composables.js +44 -0
- package/dist/context.js +25 -0
- package/dist/core/dist/animation/animator.js +72 -0
- package/dist/core/dist/animation/config.js +145 -0
- package/dist/core/dist/animation/easing.js +4 -0
- package/dist/core/dist/animation/scalar-spring.js +57 -0
- package/dist/core/dist/animation/spring.js +11 -0
- package/dist/core/dist/animation/time.js +19 -0
- package/dist/core/dist/animation/viewport-engine.js +174 -0
- package/dist/core/dist/animation/visible-range-spring.js +64 -0
- package/dist/core/dist/animation/y-range-hermite.js +72 -0
- package/dist/core/dist/animation/y-range-snap.js +31 -0
- package/dist/core/dist/animation/y-range-spring.js +59 -0
- package/dist/core/dist/axis/dom-labels.js +60 -0
- package/dist/core/dist/canvas-manager.js +109 -0
- package/dist/core/dist/chart/edge-indicators.js +26 -0
- package/dist/core/dist/chart/fit-to-data.js +9 -0
- package/dist/core/dist/chart/last-value.js +31 -0
- package/dist/core/dist/chart/options.js +35 -0
- package/dist/core/dist/chart/pan-zoom-math.js +66 -0
- package/dist/core/dist/chart/streaming-cadence.js +70 -0
- package/dist/core/dist/chart/streaming-target.js +22 -0
- package/dist/core/dist/chart/viewport-padding.js +6 -0
- package/dist/core/dist/chart/y-target.js +24 -0
- package/dist/core/dist/chart.js +1495 -0
- package/dist/core/dist/components/crosshair.js +9 -0
- package/dist/core/dist/components/edge-indicator.js +36 -0
- package/dist/core/dist/components/grid.js +22 -0
- package/dist/core/dist/components/marker.js +41 -0
- package/dist/core/dist/components/reference-line.js +48 -0
- package/dist/core/dist/components/time-region.js +35 -0
- package/dist/core/dist/data/decimation.js +54 -0
- package/dist/core/dist/data/store.js +76 -0
- package/dist/core/dist/data/sync.js +47 -0
- package/dist/core/dist/events.js +19 -0
- package/dist/core/dist/interactions/handler.js +84 -0
- package/dist/core/dist/interactions/pan.js +26 -0
- package/dist/core/dist/interactions/zoom.js +21 -0
- package/dist/core/dist/navigator/controller.js +374 -0
- package/dist/core/dist/navigator/decimate.js +44 -0
- package/dist/core/dist/navigator/interactions.js +28 -0
- package/dist/core/dist/navigator/render.js +68 -0
- package/dist/core/dist/perf/counting-context.js +19 -0
- package/dist/core/dist/perf/perf-hud.js +453 -0
- package/dist/core/dist/perf/perf-monitor.js +132 -0
- package/dist/core/dist/render-scheduler.js +31 -0
- package/dist/core/dist/scales/tick-tracker.js +110 -0
- package/dist/core/dist/scales/x-scale.js +145 -0
- package/dist/core/dist/scales/y-scale.js +156 -0
- package/dist/core/dist/series/bar.js +341 -0
- package/dist/core/dist/series/base-multi-layer.js +438 -0
- package/dist/core/dist/series/candlestick.js +481 -0
- package/dist/core/dist/series/definition.js +11 -0
- package/dist/core/dist/series/line.js +446 -0
- package/dist/core/dist/series/painters/builtins.js +36 -0
- package/dist/core/dist/series/painters/canvas-path.js +79 -0
- package/dist/core/dist/series/painters/resolve.js +32 -0
- package/dist/core/dist/series/pie.js +545 -0
- package/dist/core/dist/series/register-builtins.js +11 -0
- package/dist/core/dist/series/stack-math.js +17 -0
- package/dist/core/dist/snapshots.js +107 -0
- package/dist/core/dist/theme/create.js +176 -0
- package/dist/core/dist/theme/resolve.js +14 -0
- package/dist/core/dist/theme/themes/andromeda.js +33 -0
- package/dist/core/dist/theme/themes/ayu-mirage.js +33 -0
- package/dist/core/dist/theme/themes/catppuccin.js +33 -0
- package/dist/core/dist/theme/themes/dracula.js +35 -0
- package/dist/core/dist/theme/themes/github-light.js +34 -0
- package/dist/core/dist/theme/themes/gruvbox.js +33 -0
- package/dist/core/dist/theme/themes/handwritten.js +101 -0
- package/dist/core/dist/theme/themes/high-contrast.js +33 -0
- package/dist/core/dist/theme/themes/lavender-mist.js +33 -0
- package/dist/core/dist/theme/themes/light-pink.js +34 -0
- package/dist/core/dist/theme/themes/material-palenight.js +33 -0
- package/dist/core/dist/theme/themes/minimal-light.js +34 -0
- package/dist/core/dist/theme/themes/mint-breeze.js +33 -0
- package/dist/core/dist/theme/themes/monokai-pro.js +33 -0
- package/dist/core/dist/theme/themes/night-owl.js +35 -0
- package/dist/core/dist/theme/themes/one-dark-pro.js +33 -0
- package/dist/core/dist/theme/themes/panda.js +33 -0
- package/dist/core/dist/theme/themes/peach-cream.js +33 -0
- package/dist/core/dist/theme/themes/quiet-light.js +37 -0
- package/dist/core/dist/theme/themes/rose-pine-dawn.js +33 -0
- package/dist/core/dist/theme/themes/sand-dune.js +33 -0
- package/dist/core/dist/theme/themes/solarized-light.js +34 -0
- package/dist/core/dist/tooltip-position.js +10 -0
- package/dist/core/dist/utils/color.js +37 -0
- package/dist/core/dist/utils/format.js +29 -0
- package/dist/core/dist/utils/math.js +26 -0
- package/dist/core/dist/utils/poisoned-data-reporter.js +15 -0
- package/dist/core/dist/utils/time.js +41 -0
- package/dist/index.d.ts +1096 -63
- package/dist/index.js +142 -7754
- package/dist/ui/Crosshair.vue.js +53 -0
- package/dist/ui/Crosshair.vue2.js +4 -0
- package/dist/ui/InfoBar.vue.js +143 -0
- package/dist/ui/InfoBar.vue2.js +4 -0
- package/dist/ui/Legend.vue.js +212 -0
- package/dist/ui/Legend.vue2.js +4 -0
- package/dist/ui/Marker.vue.js +47 -0
- package/dist/ui/Marker.vue2.js +4 -0
- package/dist/ui/Navigator.vue.js +58 -0
- package/dist/ui/Navigator.vue2.js +4 -0
- package/dist/ui/NumberFlow.vue.js +69 -0
- package/dist/ui/NumberFlow.vue2.js +4 -0
- package/dist/ui/PieLegend.vue.js +132 -0
- package/dist/ui/PieLegend.vue2.js +4 -0
- package/dist/ui/PieTooltip.vue.js +135 -0
- package/dist/ui/PieTooltip.vue2.js +4 -0
- package/dist/ui/ReferenceLine.vue.js +44 -0
- package/dist/ui/ReferenceLine.vue2.js +4 -0
- package/dist/ui/TimeAxis.vue.js +43 -0
- package/dist/ui/TimeAxis.vue2.js +4 -0
- package/dist/ui/TimeRegion.vue.js +41 -0
- package/dist/ui/TimeRegion.vue2.js +4 -0
- package/dist/ui/Title.vue.js +54 -0
- package/dist/ui/Title.vue2.js +4 -0
- package/dist/ui/Tooltip.vue.js +195 -0
- package/dist/ui/Tooltip.vue2.js +4 -0
- package/dist/ui/YAxis.vue.js +42 -0
- package/dist/ui/YAxis.vue2.js +4 -0
- package/dist/ui/YLabel.vue.js +105 -0
- package/dist/ui/YLabel.vue2.js +4 -0
- package/package.json +2 -2
- package/src/BarSeries.vue +12 -13
- package/src/CandlestickSeries.vue +17 -6
- package/src/LineSeries.vue +12 -13
- package/src/PieSeries.vue +2 -1
- package/src/composables.ts +12 -2
- package/src/index.ts +42 -0
- package/src/ui/Crosshair.vue +28 -3
- package/src/ui/Legend.vue +1 -2
- package/src/ui/Marker.vue +62 -0
- package/src/ui/ReferenceLine.vue +58 -0
- package/src/ui/TimeRegion.vue +54 -0
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
import { Animator as m } from "../animation/animator.js";
|
|
2
|
+
import { ScalarSpring as g } from "../animation/scalar-spring.js";
|
|
3
|
+
import { TimeSeriesStore as p } from "../data/store.js";
|
|
4
|
+
import { resolveColor as d } from "../utils/color.js";
|
|
5
|
+
import { normalizeTimePointArray as v, normalizeTime as c } from "../utils/time.js";
|
|
6
|
+
import { sumStack as y, renderedStackPercentTop as L, renderedStackTop as V } from "./stack-math.js";
|
|
7
|
+
const A = (f, t, e) => f + (t - f) * e;
|
|
8
|
+
class D {
|
|
9
|
+
stores;
|
|
10
|
+
// --- Animation state (per layer) ----------------------------------------
|
|
11
|
+
/**
|
|
12
|
+
* Per-point entrance animation registry. Subclasses read it through
|
|
13
|
+
* {@link entranceProgress}; tests reach in directly via type-cast (kept
|
|
14
|
+
* `protected` rather than `#` so the existing test helper keeps working).
|
|
15
|
+
*/
|
|
16
|
+
entries;
|
|
17
|
+
/**
|
|
18
|
+
* Latest rendered Y for each layer's last point, chased smoothly by
|
|
19
|
+
* `updateLastPoint`. `null` while the store is empty.
|
|
20
|
+
*/
|
|
21
|
+
displayedLastValues;
|
|
22
|
+
/** Per-layer chase spring. `null` when settled or when smoothing is off. */
|
|
23
|
+
#t;
|
|
24
|
+
/**
|
|
25
|
+
* Chase displaced from the last slot by an append, still settling toward the
|
|
26
|
+
* (now-penultimate) point's stored value. Pinned to that point's `time` so
|
|
27
|
+
* {@link effectiveValue} keeps substituting the smoothed Y until it lands —
|
|
28
|
+
* killing the spring on append would snap the vertex to its raw value the
|
|
29
|
+
* very frame a new point arrives (a visible 1–2-frame kink at the line head).
|
|
30
|
+
*/
|
|
31
|
+
#e;
|
|
32
|
+
/**
|
|
33
|
+
* Per-layer alpha for the visibility cross-fade. `setAlpha` (whole series)
|
|
34
|
+
* fans out across the array; `setLayerAlpha` targets a single index. Render
|
|
35
|
+
* loops multiply the layer's alpha into `globalAlpha` per draw.
|
|
36
|
+
*/
|
|
37
|
+
#s;
|
|
38
|
+
constructor(t) {
|
|
39
|
+
this.stores = Array.from({ length: t }, () => new p()), this.entries = Array.from({ length: t }, () => /* @__PURE__ */ new Map()), this.displayedLastValues = new Array(t).fill(null), this.#t = new Array(t).fill(null), this.#e = new Array(t).fill(null), this.#s = Array.from(
|
|
40
|
+
{ length: t },
|
|
41
|
+
() => new m({ initial: 1, duration: 0, lerp: A })
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
// --- Color accessors ------------------------------------------------------
|
|
45
|
+
/**
|
|
46
|
+
* Per-layer color overrides carried in `data` (sparse — `undefined` leaves a
|
|
47
|
+
* layer on the theme base in `options.colors`). Kept separate from the base so
|
|
48
|
+
* a theme swap, which rewrites `options.colors`, leaves these untouched.
|
|
49
|
+
*/
|
|
50
|
+
#r = [];
|
|
51
|
+
setColorOverrides(t) {
|
|
52
|
+
this.#r = t;
|
|
53
|
+
}
|
|
54
|
+
/** The active resolver for a layer — the `data` override if set, else the theme base (cycled). */
|
|
55
|
+
#i(t) {
|
|
56
|
+
const e = this.#r[t];
|
|
57
|
+
if (e !== void 0) return e;
|
|
58
|
+
const s = this.options.colors;
|
|
59
|
+
return s[t % s.length];
|
|
60
|
+
}
|
|
61
|
+
/** The first layer's color resolver (string or value-fn). */
|
|
62
|
+
getColor() {
|
|
63
|
+
return this.#i(0);
|
|
64
|
+
}
|
|
65
|
+
getColors() {
|
|
66
|
+
return this.options.colors;
|
|
67
|
+
}
|
|
68
|
+
/** Resolve one layer's color for a given datum value. */
|
|
69
|
+
resolveLayerColor(t, e) {
|
|
70
|
+
return d(this.#i(t), e);
|
|
71
|
+
}
|
|
72
|
+
// --- Data ingest ----------------------------------------------------------
|
|
73
|
+
setData(t, e = 0) {
|
|
74
|
+
const s = this.stores[e];
|
|
75
|
+
if (!s) return;
|
|
76
|
+
const i = v(t ?? []);
|
|
77
|
+
s.setData(i);
|
|
78
|
+
const r = s.last();
|
|
79
|
+
this.displayedLastValues[e] = r ? r.value : null, this.#t[e] = null, this.#e[e] = null, this.entries[e].clear();
|
|
80
|
+
}
|
|
81
|
+
appendPoint(t, e = 0) {
|
|
82
|
+
const s = this.stores[e];
|
|
83
|
+
if (!s) return;
|
|
84
|
+
const i = t, r = c(i.time), n = s.last(), l = s.length;
|
|
85
|
+
s.append({ ...i, time: r });
|
|
86
|
+
const o = this.#t[e];
|
|
87
|
+
o !== null && n !== void 0 && s.length > l && (this.#e[e] = { time: n.time, anim: o }), this.displayedLastValues[e] = i.value, this.#t[e] = null;
|
|
88
|
+
const a = this.options.entryMs;
|
|
89
|
+
this.isEntryEnabled() && a > 0 && this.entries[e].set(r, { startTime: performance.now() });
|
|
90
|
+
}
|
|
91
|
+
updateLastPoint(t, e = 0) {
|
|
92
|
+
const s = this.stores[e];
|
|
93
|
+
if (!s) return;
|
|
94
|
+
const i = t;
|
|
95
|
+
s.updateLast({ ...i, time: c(i.time) });
|
|
96
|
+
const r = i.value, n = this.options.smoothMs;
|
|
97
|
+
if (n <= 0) {
|
|
98
|
+
this.displayedLastValues[e] = r, this.#t[e] = null;
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
let l = this.#t[e];
|
|
102
|
+
if (l === null) {
|
|
103
|
+
const o = this.displayedLastValues[e] ?? r;
|
|
104
|
+
l = new g(o), this.#t[e] = l;
|
|
105
|
+
}
|
|
106
|
+
l.retarget(r, { settleMs: n });
|
|
107
|
+
}
|
|
108
|
+
keepLast(t, e = 0) {
|
|
109
|
+
const s = this.stores[e];
|
|
110
|
+
if (!s || t < 0) return;
|
|
111
|
+
const i = s.length - t;
|
|
112
|
+
i <= 0 || s.trimStart(i);
|
|
113
|
+
}
|
|
114
|
+
// --- Layer model ----------------------------------------------------------
|
|
115
|
+
getLayerCount() {
|
|
116
|
+
return this.stores.length;
|
|
117
|
+
}
|
|
118
|
+
setLayerVisible(t, e) {
|
|
119
|
+
this.stores[t]?.setVisible(e);
|
|
120
|
+
}
|
|
121
|
+
isLayerVisible(t) {
|
|
122
|
+
return this.stores[t]?.isVisible() ?? !0;
|
|
123
|
+
}
|
|
124
|
+
/** Representative concrete color per layer — value-fns are resolved at the
|
|
125
|
+
* layer's last value, giving the legend swatch / color-change detection a
|
|
126
|
+
* stable string. Honors per-layer `data` overrides. */
|
|
127
|
+
getLayerColors() {
|
|
128
|
+
const t = [];
|
|
129
|
+
for (let e = 0; e < this.stores.length; e++) {
|
|
130
|
+
const s = this.stores[e]?.last()?.value ?? 0;
|
|
131
|
+
t.push(this.resolveLayerColor(e, s));
|
|
132
|
+
}
|
|
133
|
+
return t;
|
|
134
|
+
}
|
|
135
|
+
// --- Lifecycle ------------------------------------------------------------
|
|
136
|
+
onDataChanged(t) {
|
|
137
|
+
for (const e of this.stores) e.on("update", t);
|
|
138
|
+
return () => {
|
|
139
|
+
for (const e of this.stores) e.off("update", t);
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
dispose() {
|
|
143
|
+
for (const t of this.stores) t.removeAllListeners();
|
|
144
|
+
}
|
|
145
|
+
// --- Animation primitives — renderer-owned state --------------------------
|
|
146
|
+
/**
|
|
147
|
+
* Per-point entrance progress in `[0, 1]`. Reads the local entry registry,
|
|
148
|
+
* returning `1` for a settled or absent entry (which matches the visual
|
|
149
|
+
* default: nothing to fade in).
|
|
150
|
+
*/
|
|
151
|
+
entranceProgress(t, e, s) {
|
|
152
|
+
const i = this.entries[e]?.get(s);
|
|
153
|
+
if (i === void 0) return 1;
|
|
154
|
+
const r = this.options.entryMs;
|
|
155
|
+
if (r <= 0) return 1;
|
|
156
|
+
const n = performance.now() - i.startTime;
|
|
157
|
+
return n <= 0 ? 0 : n >= r ? 1 : n / r;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Trailing data points whose entrance is still unsettled, oldest first.
|
|
161
|
+
* Walks back from the store's last point and stops at the first settled
|
|
162
|
+
* point — that point is the stable anchor a chained grow-lerp hangs off.
|
|
163
|
+
*
|
|
164
|
+
* A feed that appends faster than `entryMs` keeps several entrances in
|
|
165
|
+
* flight at once; animating only the newest (and snapping the rest to
|
|
166
|
+
* their raw spots) makes the head region jump a segment-fraction forward
|
|
167
|
+
* on every append. Renderers lerp each chain link from the previous
|
|
168
|
+
* link's *rendered* position instead, so geometry stays continuous.
|
|
169
|
+
*/
|
|
170
|
+
unsettledTail(t, e) {
|
|
171
|
+
const s = this.stores[e]?.getAll();
|
|
172
|
+
if (!s || s.length < 2) return [];
|
|
173
|
+
const i = [];
|
|
174
|
+
for (let r = s.length - 1; r >= 1; r--) {
|
|
175
|
+
const n = this.entranceProgress(t, e, s[r].time);
|
|
176
|
+
if (n >= 1) break;
|
|
177
|
+
i.unshift({ time: s[r].time, progress: n });
|
|
178
|
+
}
|
|
179
|
+
return i;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Substitute the renderer-smoothed last value for `rawValue` when the
|
|
183
|
+
* query `time` matches the layer's current last point, or the still-settling
|
|
184
|
+
* pinned chase (see {@link appendPoint}) when it matches the penultimate.
|
|
185
|
+
* Falls back to `rawValue` when the store is empty or no smoothing has
|
|
186
|
+
* happened yet.
|
|
187
|
+
*/
|
|
188
|
+
effectiveValue(t, e, s, i) {
|
|
189
|
+
const r = this.stores[e]?.last()?.time;
|
|
190
|
+
if (r !== void 0 && s === r)
|
|
191
|
+
return this.displayedLastValues[e] ?? i;
|
|
192
|
+
const n = this.#e[e];
|
|
193
|
+
return n !== null && n.time === s ? n.anim.current : i;
|
|
194
|
+
}
|
|
195
|
+
// --- Animation lifecycle --------------------------------------------------
|
|
196
|
+
/**
|
|
197
|
+
* Advance owned animators against `now` and prune fully-settled entries.
|
|
198
|
+
* Called by the subclass's `render()` once per frame, before drawing. The
|
|
199
|
+
* subclass passes its own clock so test harnesses (which stub
|
|
200
|
+
* `performance.now`) can drive progression deterministically.
|
|
201
|
+
*/
|
|
202
|
+
tickAnimations(t) {
|
|
203
|
+
for (const e of this.#s) e.tick(t);
|
|
204
|
+
for (let e = 0; e < this.stores.length; e++) {
|
|
205
|
+
const s = this.#t[e];
|
|
206
|
+
if (s !== null) {
|
|
207
|
+
const l = s.tick(t);
|
|
208
|
+
this.displayedLastValues[e] = s.current, l || (this.#t[e] = null);
|
|
209
|
+
}
|
|
210
|
+
const i = this.#e[e];
|
|
211
|
+
i !== null && !i.anim.tick(t) && (this.#e[e] = null);
|
|
212
|
+
const r = this.options.entryMs;
|
|
213
|
+
if (r <= 0) {
|
|
214
|
+
this.entries[e].clear();
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
const n = this.entries[e];
|
|
218
|
+
for (const [l, o] of n)
|
|
219
|
+
t - o.startTime >= r && n.delete(l);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/** True while any layer has an active chase, unsettled entry, or alpha fade. */
|
|
223
|
+
get needsAnimation() {
|
|
224
|
+
for (const t of this.#s)
|
|
225
|
+
if (t.animating) return !0;
|
|
226
|
+
for (const t of this.#t)
|
|
227
|
+
if (t !== null) return !0;
|
|
228
|
+
for (const t of this.#e)
|
|
229
|
+
if (t !== null) return !0;
|
|
230
|
+
for (const t of this.entries)
|
|
231
|
+
if (t.size > 0) return !0;
|
|
232
|
+
return !1;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Abort in-flight per-point entrance animations on every layer. Live-value
|
|
236
|
+
* chase (`displayedLastValues`) is intentionally left alone — its motion
|
|
237
|
+
* is subtle and shouldn't jump when the viewport moves.
|
|
238
|
+
*/
|
|
239
|
+
cancelEntranceAnimations() {
|
|
240
|
+
for (const t of this.entries) t.clear();
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Start a series-wide fade toward `target` over `durationMs`. Fans out
|
|
244
|
+
* across every per-layer animator so a `setSeriesVisible` toggle is just
|
|
245
|
+
* "set every layer to the same target". `durationMs <= 0` snaps.
|
|
246
|
+
*/
|
|
247
|
+
setAlpha(t, e) {
|
|
248
|
+
for (const s of this.#s)
|
|
249
|
+
s.setTarget(t, { duration: e });
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* `1` while any layer has alpha > 0 OR is animating, `0` when every layer
|
|
253
|
+
* has fully faded and is at rest. Per-layer alpha is composed into
|
|
254
|
+
* `globalAlpha` by the renderer's own draw loops, so this stays a binary
|
|
255
|
+
* skip-gate at the chart level.
|
|
256
|
+
*
|
|
257
|
+
* The `animating` check matters when every layer is at `current = 0` and
|
|
258
|
+
* the user toggles one back in: `setLayerAlpha(idx, 1, ms)` flips the
|
|
259
|
+
* animator to `animating = true` but `current` is still `0` until the next
|
|
260
|
+
* `tick`. If chart skipped the render based on `current` alone, the
|
|
261
|
+
* animator would never tick (render is what advances it), and the fade-in
|
|
262
|
+
* would deadlock at `0`.
|
|
263
|
+
*/
|
|
264
|
+
getAlpha() {
|
|
265
|
+
for (const t of this.#s)
|
|
266
|
+
if (t.current > 0 || t.animating) return 1;
|
|
267
|
+
return 0;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Start a fade for a single layer toward `target` over `durationMs`.
|
|
271
|
+
* Subclass draw loops multiply this into `globalAlpha` per layer so the
|
|
272
|
+
* fade lives next to the geometry it affects.
|
|
273
|
+
*/
|
|
274
|
+
setLayerAlpha(t, e, s) {
|
|
275
|
+
this.#s[t]?.setTarget(e, { duration: s });
|
|
276
|
+
}
|
|
277
|
+
/** Latest rendered per-layer alpha. Defaults to 1 for out-of-range indices. */
|
|
278
|
+
getLayerAlpha(t) {
|
|
279
|
+
return this.#s[t]?.current ?? 1;
|
|
280
|
+
}
|
|
281
|
+
// --- Data queries ---------------------------------------------------------
|
|
282
|
+
getLastValue() {
|
|
283
|
+
for (let t = this.stores.length - 1; t >= 0; t--) {
|
|
284
|
+
const e = this.stores[t].last();
|
|
285
|
+
if (e) return e.value;
|
|
286
|
+
}
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
getDataAtTime(t, e) {
|
|
290
|
+
return this.stores[0]?.findNearest(t, e) ?? null;
|
|
291
|
+
}
|
|
292
|
+
getLayerSnapshots(t, e) {
|
|
293
|
+
if (this.stores.length <= 1) return null;
|
|
294
|
+
const s = [];
|
|
295
|
+
for (let i = 0; i < this.stores.length; i++) {
|
|
296
|
+
if (!this.stores[i].isVisible()) continue;
|
|
297
|
+
const r = this.stores[i].getVisibleData(t - e, t + e);
|
|
298
|
+
if (r.length === 0) continue;
|
|
299
|
+
let n = r[0], l = Math.abs(r[0].time - t);
|
|
300
|
+
for (let o = 1; o < r.length; o++) {
|
|
301
|
+
const a = Math.abs(r[o].time - t);
|
|
302
|
+
a <= l && (l = a, n = r[o]);
|
|
303
|
+
}
|
|
304
|
+
s.push({
|
|
305
|
+
layerIndex: i,
|
|
306
|
+
time: n.time,
|
|
307
|
+
value: n.value,
|
|
308
|
+
// Resolve the layer's color at the hovered datum's value so a value-fn
|
|
309
|
+
// colors the tooltip dot to match the bar / segment under the cursor.
|
|
310
|
+
color: this.resolveLayerColor(i, n.value)
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
return s.length > 0 ? s : null;
|
|
314
|
+
}
|
|
315
|
+
getStackedLastValue() {
|
|
316
|
+
if (this.stores.length <= 1) {
|
|
317
|
+
const r = this.stores[0]?.last();
|
|
318
|
+
return r ? { value: r.value, isLive: !0 } : null;
|
|
319
|
+
}
|
|
320
|
+
let t = -1 / 0;
|
|
321
|
+
for (const r of this.stores) {
|
|
322
|
+
if (!r.isVisible()) continue;
|
|
323
|
+
const n = r.last();
|
|
324
|
+
n && n.time > t && (t = n.time);
|
|
325
|
+
}
|
|
326
|
+
if (t === -1 / 0) return null;
|
|
327
|
+
const e = this.options.stacking;
|
|
328
|
+
if (e === "off") {
|
|
329
|
+
for (let r = this.stores.length - 1; r >= 0; r--) {
|
|
330
|
+
if (!this.stores[r].isVisible()) continue;
|
|
331
|
+
const n = this.stores[r].last();
|
|
332
|
+
if (n) return { value: n.value, isLive: !0 };
|
|
333
|
+
}
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
const s = [];
|
|
337
|
+
for (const r of this.stores) {
|
|
338
|
+
if (!r.isVisible()) continue;
|
|
339
|
+
const n = r.last();
|
|
340
|
+
s.push(n && n.time === t ? n.value : 0);
|
|
341
|
+
}
|
|
342
|
+
const i = y(s);
|
|
343
|
+
return { value: e === "percent" ? L(i) : V(i), isLive: !0 };
|
|
344
|
+
}
|
|
345
|
+
getLayerLastSnapshots() {
|
|
346
|
+
if (this.stores.length <= 1) return null;
|
|
347
|
+
const t = [];
|
|
348
|
+
for (let e = 0; e < this.stores.length; e++) {
|
|
349
|
+
if (!this.stores[e].isVisible()) continue;
|
|
350
|
+
const s = this.stores[e].last();
|
|
351
|
+
s && t.push({
|
|
352
|
+
layerIndex: e,
|
|
353
|
+
time: s.time,
|
|
354
|
+
value: s.value,
|
|
355
|
+
color: this.resolveLayerColor(e, s.value)
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
return t.length > 0 ? t : null;
|
|
359
|
+
}
|
|
360
|
+
getTotalLength() {
|
|
361
|
+
let t = 0;
|
|
362
|
+
for (const e of this.stores) t += e.length;
|
|
363
|
+
return t;
|
|
364
|
+
}
|
|
365
|
+
// --- TimeSeriesRenderer data queries --------------------------------------
|
|
366
|
+
/** Earliest first-sample / latest last-sample across ALL layers, skipping
|
|
367
|
+
* empty ones. Aggregating every layer (not just `stores[0]`) is what fixes
|
|
368
|
+
* the multi-layer time-range leak: a populated layer 1 with an empty layer 0
|
|
369
|
+
* still reports the real bounds. */
|
|
370
|
+
getTimeBounds() {
|
|
371
|
+
let t = 1 / 0, e = -1 / 0;
|
|
372
|
+
for (const s of this.stores) {
|
|
373
|
+
const i = s.first();
|
|
374
|
+
if (!i) continue;
|
|
375
|
+
const r = s.last() ?? i;
|
|
376
|
+
i.time < t && (t = i.time), r.time > e && (e = r.time);
|
|
377
|
+
}
|
|
378
|
+
return t === 1 / 0 ? null : { first: t, last: e };
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Last data point of **layer 0 (`stores[0]`) only** — preserves the behavior
|
|
382
|
+
* of the deleted `get store()` getter. NOTE: this is intentionally NOT the
|
|
383
|
+
* same as {@link getStackedLastValue}, which for non-stacked multi-layer
|
|
384
|
+
* reports the top visible layer and for stacked reports the cumulative sum.
|
|
385
|
+
* Last-data / previous-close semantics stay pinned to layer 0 for back-compat.
|
|
386
|
+
*/
|
|
387
|
+
getLastDataPoint() {
|
|
388
|
+
return this.stores[0]?.last() ?? null;
|
|
389
|
+
}
|
|
390
|
+
/** Second-to-last point of layer 0 (`stores[0]`). See {@link getLastDataPoint}. */
|
|
391
|
+
getSecondLastDataPoint() {
|
|
392
|
+
const t = this.stores[0]?.getAll();
|
|
393
|
+
return !t || t.length < 2 ? null : t[t.length - 2];
|
|
394
|
+
}
|
|
395
|
+
/** First `maxCount` sample times from the first populated layer. */
|
|
396
|
+
sampleTimes(t) {
|
|
397
|
+
for (const e of this.stores) {
|
|
398
|
+
const s = e.getAll();
|
|
399
|
+
if (s.length !== 0)
|
|
400
|
+
return s.slice(0, t).map((i) => i.time);
|
|
401
|
+
}
|
|
402
|
+
return [];
|
|
403
|
+
}
|
|
404
|
+
/** Visible points of layer 0 (`stores[0]`). See {@link getLastDataPoint}. */
|
|
405
|
+
getVisibleDataPoints(t, e) {
|
|
406
|
+
return this.stores[0]?.getVisibleData(t, e) ?? [];
|
|
407
|
+
}
|
|
408
|
+
getValueRange(t, e) {
|
|
409
|
+
const s = this.options.stacking;
|
|
410
|
+
if (s === "percent")
|
|
411
|
+
return { min: 0, max: 100 };
|
|
412
|
+
const i = this.stores.map((o) => o.isVisible() ? o.getVisibleData(t, e) : []);
|
|
413
|
+
if (s === "off") {
|
|
414
|
+
let o = 1 / 0, a = -1 / 0;
|
|
415
|
+
for (const u of i)
|
|
416
|
+
for (const h of u)
|
|
417
|
+
Number.isFinite(h.value) && (h.value < o && (o = h.value), h.value > a && (a = h.value));
|
|
418
|
+
return o < 1 / 0 ? { min: o, max: a } : null;
|
|
419
|
+
}
|
|
420
|
+
const r = /* @__PURE__ */ new Map();
|
|
421
|
+
for (let o = 0; o < i.length; o++)
|
|
422
|
+
for (const a of i[o]) {
|
|
423
|
+
let u = r.get(a.time);
|
|
424
|
+
u || (u = new Array(i.length).fill(0), r.set(a.time, u)), u[o] = Number.isFinite(a.value) ? a.value : 0;
|
|
425
|
+
}
|
|
426
|
+
let n = 0, l = 0;
|
|
427
|
+
for (const o of r.values()) {
|
|
428
|
+
let a = 0, u = 0;
|
|
429
|
+
for (const h of o)
|
|
430
|
+
h > 0 ? a += h : u += h;
|
|
431
|
+
a > l && (l = a), u < n && (n = u);
|
|
432
|
+
}
|
|
433
|
+
return l > n ? { min: n, max: l } : null;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
export {
|
|
437
|
+
D as BaseMultiLayerSeries
|
|
438
|
+
};
|