@wick-charts/vue 0.8.0 → 0.8.2

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 (48) hide show
  1. package/README.md +2 -0
  2. package/dist/BandSeries.vue.js +43 -32
  3. package/dist/BarSeries.vue.js +44 -33
  4. package/dist/CandlestickSeries.vue.js +24 -23
  5. package/dist/ChartContainer.vue.js +1 -1
  6. package/dist/HeatmapSeries.vue.js +21 -20
  7. package/dist/LineSeries.vue.js +43 -32
  8. package/dist/PieSeries.vue.js +25 -24
  9. package/dist/composables.js +63 -36
  10. package/dist/core/dist/axis/dom-labels.js +64 -53
  11. package/dist/core/dist/chart/badge-animator.js +38 -28
  12. package/dist/core/dist/chart/options.js +1 -1
  13. package/dist/core/dist/chart/y-target.js +38 -32
  14. package/dist/core/dist/chart.js +852 -709
  15. package/dist/core/dist/data/decimation.js +52 -51
  16. package/dist/core/dist/data/store.js +38 -20
  17. package/dist/core/dist/data/sync.js +97 -59
  18. package/dist/core/dist/navigator/controller.js +11 -11
  19. package/dist/core/dist/perf/perf-hud.js +3 -3
  20. package/dist/core/dist/scales/tick-tracker.js +2 -1
  21. package/dist/core/dist/scales/time-ticks.js +99 -0
  22. package/dist/core/dist/scales/x-scale.js +33 -27
  23. package/dist/core/dist/series/band.js +83 -83
  24. package/dist/core/dist/series/bar.js +240 -211
  25. package/dist/core/dist/series/base-multi-layer.js +288 -240
  26. package/dist/core/dist/series/candlestick.js +168 -168
  27. package/dist/core/dist/series/line.js +545 -455
  28. package/dist/core/dist/snapshots.js +109 -86
  29. package/dist/core/dist/utils/point.js +4 -0
  30. package/dist/core/dist/utils/time.js +42 -26
  31. package/dist/index.d.ts +67 -28
  32. package/dist/ui/InfoBar.vue.js +69 -69
  33. package/dist/ui/NumberFlow.vue.js +36 -27
  34. package/dist/ui/Tooltip.vue.js +128 -131
  35. package/dist/ui/YLabel.vue.js +47 -47
  36. package/package.json +2 -2
  37. package/src/BandSeries.vue +20 -5
  38. package/src/BarSeries.vue +20 -5
  39. package/src/CandlestickSeries.vue +5 -4
  40. package/src/ChartContainer.vue +15 -9
  41. package/src/HeatmapSeries.vue +5 -4
  42. package/src/LineSeries.vue +20 -5
  43. package/src/PieSeries.vue +6 -5
  44. package/src/composables.ts +64 -1
  45. package/src/ui/InfoBar.vue +11 -3
  46. package/src/ui/NumberFlow.vue +17 -3
  47. package/src/ui/Tooltip.vue +23 -12
  48. package/src/ui/YLabel.vue +28 -27
@@ -1,12 +1,12 @@
1
- import { Animator as k } from "../animation/animator.js";
1
+ import { Animator as b } from "../animation/animator.js";
2
2
  import { IntroWave as y } from "../animation/intro-wave.js";
3
3
  import { ScalarSpring as V } from "../animation/scalar-spring.js";
4
- import { TimeSeriesStore as M } from "../data/store.js";
5
- import { resolveColor as W } from "../utils/color.js";
6
- import { normalizeTimePointArray as d, normalizeTime as L } from "../utils/time.js";
7
- import { sumStack as A, renderedStackPercentTop as b, renderedStackTop as w } from "./stack-math.js";
8
- const T = (m, e, t) => m + (e - m) * t;
9
- class F {
4
+ import { TimeSeriesStore as P } from "../data/store.js";
5
+ import { resolveColor as S } from "../utils/color.js";
6
+ import { normalizeTimePointArray as d, normalizeTime as L, sliceSample as M } from "../utils/time.js";
7
+ import { sumStack as w, renderedStackPercentTop as A, renderedStackTop as k } from "./stack-math.js";
8
+ const F = (m, t, e) => m + (t - m) * e;
9
+ class I {
10
10
  stores;
11
11
  // --- Animation state (per layer) ----------------------------------------
12
12
  /**
@@ -53,10 +53,10 @@ class F {
53
53
  * (the pre-prepend first point). Stagger position is `0` at the boundary
54
54
  * and `1` at the deepest point. */
55
55
  historyRange = null;
56
- constructor(e) {
57
- this.stores = Array.from({ length: e }, () => new M()), this.entries = Array.from({ length: e }, () => /* @__PURE__ */ new Map()), this.displayedLastValues = new Array(e).fill(null), this.#t = new Array(e).fill(null), this.#e = new Array(e).fill(null), this.#s = Array.from({ length: e }, () => /* @__PURE__ */ new Map()), this.#i = Array.from(
58
- { length: e },
59
- () => new k({ initial: 1, duration: 0, lerp: T })
56
+ constructor(t) {
57
+ 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({ length: t }, () => /* @__PURE__ */ new Map()), this.#i = Array.from(
58
+ { length: t },
59
+ () => new b({ initial: 1, duration: 0, lerp: F })
60
60
  );
61
61
  }
62
62
  // --- Color accessors ------------------------------------------------------
@@ -65,91 +65,91 @@ class F {
65
65
  * layer on the theme base in `options.colors`). Kept separate from the base so
66
66
  * a theme swap, which rewrites `options.colors`, leaves these untouched.
67
67
  */
68
- #r = [];
69
- setColorOverrides(e) {
70
- this.#r = e;
68
+ #n = [];
69
+ setColorOverrides(t) {
70
+ this.#n = t;
71
71
  }
72
72
  /** The active resolver for a layer — the `data` override if set, else the theme base (cycled). */
73
- #n(e) {
74
- const t = this.#r[e];
75
- if (t !== void 0) return t;
73
+ #r(t) {
74
+ const e = this.#n[t];
75
+ if (e !== void 0) return e;
76
76
  const s = this.options.colors;
77
- return s[e % s.length];
77
+ return s[t % s.length];
78
78
  }
79
79
  /** The first layer's color resolver (string or value-fn). */
80
80
  getColor() {
81
- return this.#n(0);
81
+ return this.#r(0);
82
82
  }
83
83
  getColors() {
84
84
  return this.options.colors;
85
85
  }
86
86
  /** Resolve one layer's color for a given datum value. */
87
- resolveLayerColor(e, t) {
88
- return W(this.#n(e), t);
87
+ resolveLayerColor(t, e) {
88
+ return S(this.#r(t), e);
89
89
  }
90
90
  // --- Data ingest ----------------------------------------------------------
91
- setData(e, t = 0) {
92
- const s = this.stores[t];
91
+ setData(t, e = 0) {
92
+ const s = this.stores[e];
93
93
  if (!s) return;
94
94
  let i = !1;
95
95
  for (const o of this.stores)
96
96
  o.length > 0 && (i = !0);
97
- const r = d(e ?? []);
98
- s.setData(r), !i && r.length > 0 && this.introWave.arm(this.options.introMs);
99
- const n = s.last();
100
- this.displayedLastValues[t] = n ? n.value : null, this.#t[t] = null, this.#e[t] = null, this.#s[t].clear(), this.entries[t].clear(), this.historyWave.finish(), this.historyRange = null;
97
+ const n = d(t ?? []);
98
+ !i && n.length > 0 && this.introWave.arm(this.options.introMs), s.setData(n);
99
+ const r = s.last();
100
+ this.displayedLastValues[e] = r ? r.value : null, this.#t[e] = null, this.#e[e] = null, this.#s[e].clear(), this.entries[e].clear(), this.historyWave.finish(), this.historyRange = null;
101
101
  }
102
102
  /**
103
103
  * Replace a layer like {@link setData}, but ease each point that keeps its
104
104
  * `time` from the value on screen to its new one over `smoothMs`; a point
105
105
  * with a new `time` plays its entrance.
106
106
  */
107
- morphData(e, t = 0) {
108
- const s = this.stores[t];
107
+ morphData(t, e = 0) {
108
+ const s = this.stores[e];
109
109
  if (!s) return;
110
110
  if (!this.stores.some((h) => h.length > 0)) {
111
- this.setData(e, t);
111
+ this.setData(t, e);
112
112
  return;
113
113
  }
114
114
  const i = /* @__PURE__ */ new Map();
115
115
  for (const h of s.getAll())
116
- i.set(h.time, this.liveValue(t, h.time, h.value));
117
- const r = d(e ?? []);
118
- s.setData(r);
119
- const n = this.options.smoothMs, o = this.isEntryEnabled() && this.options.entryMs > 0, l = this.entries[t], u = this.#s[t], a = /* @__PURE__ */ new Map();
120
- for (const h of r) {
116
+ i.set(h.time, this.liveValue(e, h.time, h.value));
117
+ const n = d(t ?? []);
118
+ s.setData(n);
119
+ const r = this.options.smoothMs, o = this.isEntryEnabled() && this.options.entryMs > 0, l = this.entries[e], u = this.#s[e], a = /* @__PURE__ */ new Map();
120
+ for (const h of n) {
121
121
  const g = i.get(h.time);
122
122
  if (g === void 0) {
123
123
  o && l.set(h.time, { startTime: performance.now() });
124
124
  continue;
125
125
  }
126
126
  const p = h.value;
127
- if (g === p || n <= 0 || !Number.isFinite(g) || !Number.isFinite(p)) continue;
127
+ if (g === p || r <= 0 || !Number.isFinite(g) || !Number.isFinite(p)) continue;
128
128
  const v = u.get(h.time) ?? new V(g);
129
- v.target !== p && v.retarget(p, { settleMs: n }), a.set(h.time, v);
129
+ v.target !== p && v.retarget(p, { settleMs: r }), a.set(h.time, v);
130
130
  }
131
- const c = new Set(r.map((h) => h.time));
131
+ const c = new Set(n.map((h) => h.time));
132
132
  for (const h of l.keys())
133
133
  c.has(h) || l.delete(h);
134
- this.#s[t] = a;
134
+ this.#s[e] = a;
135
135
  const f = s.last();
136
- this.displayedLastValues[t] = f ? f.value : null, this.#t[t] = null, this.#e[t] = null;
136
+ this.displayedLastValues[e] = f ? f.value : null, this.#t[e] = null, this.#e[e] = null;
137
137
  }
138
- appendPoint(e, t = 0) {
139
- const s = this.stores[t];
138
+ appendPoint(t, e = 0) {
139
+ const s = this.stores[e];
140
140
  if (!s) return;
141
- const i = e, r = L(i.time), n = s.last(), o = s.length;
142
- s.append({ ...i, time: r });
143
- const l = this.#t[t];
144
- l !== null && n !== void 0 && s.length > o && (this.#e[t] = { time: n.time, anim: l }), this.displayedLastValues[t] = i.value, this.#t[t] = null;
141
+ const i = t, n = L(i.time), r = s.last(), o = s.length;
142
+ s.append({ ...i, time: n });
143
+ const l = this.#t[e];
144
+ l !== null && r !== void 0 && s.length > o && (this.#e[e] = { time: r.time, anim: l }), this.displayedLastValues[e] = i.value, this.#t[e] = null;
145
145
  const u = this.options.entryMs;
146
- this.isEntryEnabled() && u > 0 && this.entries[t].set(r, { startTime: performance.now() });
146
+ this.isEntryEnabled() && u > 0 && this.entries[e].set(n, { startTime: performance.now() });
147
147
  }
148
- prependPoints(e, t = 0) {
149
- const s = this.stores[t];
150
- if (!s || e.length === 0) return;
151
- const i = d(e), r = s.first()?.time;
152
- s.prepend(i), this.#o(i[0]?.time, r);
148
+ prependPoints(t, e = 0) {
149
+ const s = this.stores[e];
150
+ if (!s || t.length === 0) return;
151
+ const i = d(t), n = s.first()?.time;
152
+ s.prepend(i), this.#o(i[0]?.time, n);
153
153
  }
154
154
  /**
155
155
  * Arm (or extend) the history-reveal wave for freshly prepended points.
@@ -157,71 +157,71 @@ class F {
157
157
  * another layer — only the range grows; restarting the clock would snap
158
158
  * half-revealed points back to invisible.
159
159
  */
160
- #o(e, t) {
160
+ #o(t, e) {
161
161
  const s = this.options.historyRevealMs;
162
- if (!(this.options.historyReveal === "none" || s <= 0) && !(e === void 0 || t === void 0)) {
162
+ if (!(this.options.historyReveal === "none" || s <= 0) && !(t === void 0 || e === void 0)) {
163
163
  if (this.historyWave.active && this.historyRange !== null) {
164
- this.historyRange = { from: Math.min(this.historyRange.from, e), to: this.historyRange.to };
164
+ this.historyRange = { from: Math.min(this.historyRange.from, t), to: this.historyRange.to };
165
165
  return;
166
166
  }
167
- this.historyRange = { from: e, to: t }, this.historyWave.arm(s);
167
+ this.historyRange = { from: t, to: e }, this.historyWave.arm(s);
168
168
  }
169
169
  }
170
- updateLastPoint(e, t = 0) {
171
- const s = this.stores[t];
170
+ updateLastPoint(t, e = 0) {
171
+ const s = this.stores[e];
172
172
  if (!s) return;
173
- const i = e, r = s.last()?.time;
173
+ const i = t, n = s.last()?.time;
174
174
  s.updateLast({ ...i, time: L(i.time) });
175
- const n = this.#s[t], o = r === void 0 ? void 0 : n.get(r);
176
- r !== void 0 && o !== void 0 && (n.delete(r), this.displayedLastValues[t] = o.current, this.#t[t] = o);
175
+ const r = this.#s[e], o = n === void 0 ? void 0 : r.get(n);
176
+ n !== void 0 && o !== void 0 && (r.delete(n), this.displayedLastValues[e] = o.current, this.#t[e] = o);
177
177
  const l = i.value, u = this.options.smoothMs;
178
178
  if (u <= 0) {
179
- this.displayedLastValues[t] = l, this.#t[t] = null;
179
+ this.displayedLastValues[e] = l, this.#t[e] = null;
180
180
  return;
181
181
  }
182
- let a = this.#t[t];
182
+ let a = this.#t[e];
183
183
  if (a === null) {
184
- const c = this.displayedLastValues[t] ?? l;
185
- a = new V(c), this.#t[t] = a;
184
+ const c = this.displayedLastValues[e] ?? l;
185
+ a = new V(c), this.#t[e] = a;
186
186
  }
187
187
  a.retarget(l, { settleMs: u });
188
188
  }
189
- keepLast(e, t = 0) {
190
- const s = this.stores[t];
191
- if (!s || e < 0) return;
192
- const i = s.length - e;
189
+ keepLast(t, e = 0) {
190
+ const s = this.stores[e];
191
+ if (!s || t < 0) return;
192
+ const i = s.length - t;
193
193
  i <= 0 || s.trimStart(i);
194
194
  }
195
195
  // --- Layer model ----------------------------------------------------------
196
196
  getLayerCount() {
197
197
  return this.stores.length;
198
198
  }
199
- setLayerVisible(e, t) {
200
- this.stores[e]?.setVisible(t);
199
+ setLayerVisible(t, e) {
200
+ this.stores[t]?.setVisible(e);
201
201
  }
202
- isLayerVisible(e) {
203
- return this.stores[e]?.isVisible() ?? !0;
202
+ isLayerVisible(t) {
203
+ return this.stores[t]?.isVisible() ?? !0;
204
204
  }
205
205
  /** Representative concrete color per layer — value-fns are resolved at the
206
206
  * layer's last value, giving the legend swatch / color-change detection a
207
207
  * stable string. Honors per-layer `data` overrides. */
208
208
  getLayerColors() {
209
- const e = [];
210
- for (let t = 0; t < this.stores.length; t++) {
211
- const s = this.stores[t]?.last()?.value ?? 0;
212
- e.push(this.resolveLayerColor(t, s));
209
+ const t = [];
210
+ for (let e = 0; e < this.stores.length; e++) {
211
+ const s = this.lastFinitePoint(e)?.value ?? 0;
212
+ t.push(this.resolveLayerColor(e, s));
213
213
  }
214
- return e;
214
+ return t;
215
215
  }
216
216
  // --- Lifecycle ------------------------------------------------------------
217
- onDataChanged(e) {
218
- for (const t of this.stores) t.on("update", e);
217
+ onDataChanged(t) {
218
+ for (const e of this.stores) e.on("update", t);
219
219
  return () => {
220
- for (const t of this.stores) t.off("update", e);
220
+ for (const e of this.stores) e.off("update", t);
221
221
  };
222
222
  }
223
223
  dispose() {
224
- for (const e of this.stores) e.removeAllListeners();
224
+ for (const t of this.stores) t.removeAllListeners();
225
225
  }
226
226
  // --- Animation primitives — renderer-owned state --------------------------
227
227
  /**
@@ -231,13 +231,13 @@ class F {
231
231
  * pure function of wall time — so tooltip/last-value queries (which have
232
232
  * no context to hand) can read it too.
233
233
  */
234
- entranceProgress(e, t) {
235
- const s = this.entries[e]?.get(t);
234
+ entranceProgress(t, e) {
235
+ const s = this.entries[t]?.get(e);
236
236
  if (s === void 0) return 1;
237
237
  const i = this.options.entryMs;
238
238
  if (i <= 0) return 1;
239
- const r = performance.now() - s.startTime;
240
- return r <= 0 ? 0 : r >= i ? 1 : r / i;
239
+ const n = performance.now() - s.startTime;
240
+ return n <= 0 ? 0 : n >= i ? 1 : n / i;
241
241
  }
242
242
  /**
243
243
  * Initial-load reveal progress for a datum, keyed by its normalized
@@ -247,10 +247,10 @@ class F {
247
247
  * (bar) fold this into their {@link entranceProgress} override; the line
248
248
  * renders its own continuous sweep instead.
249
249
  */
250
- introProgressAt(e, t) {
250
+ introProgressAt(t, e) {
251
251
  if (!this.introWave.active) return 1;
252
- const s = e.timeScale.getRange(), i = s.to - s.from, r = i > 0 ? (t - s.from) / i : 0;
253
- return this.introWave.progressAt(r);
252
+ const s = t.timeScale.getRange(), i = s.to - s.from, n = i > 0 ? (e - s.from) / i : 0;
253
+ return this.introWave.progressAt(n);
254
254
  }
255
255
  /**
256
256
  * History-prepend reveal progress for a datum. Stagger position is
@@ -258,10 +258,10 @@ class F {
258
258
  * point — so the wave travels into history. `1` for points outside the
259
259
  * prepended span or when no reveal is in flight.
260
260
  */
261
- historyProgressAt(e) {
262
- const t = this.historyRange;
263
- if (!this.historyWave.active || t === null || e >= t.to) return 1;
264
- const s = t.to - t.from, i = s > 0 ? (t.to - e) / s : 0;
261
+ historyProgressAt(t) {
262
+ const e = this.historyRange;
263
+ if (!this.historyWave.active || e === null || t >= e.to) return 1;
264
+ const s = e.to - e.from, i = s > 0 ? (e.to - t) / s : 0;
265
265
  return this.historyWave.progressAt(i);
266
266
  }
267
267
  /**
@@ -275,14 +275,14 @@ class F {
275
275
  * on every append. Renderers lerp each chain link from the previous
276
276
  * link's *rendered* position instead, so geometry stays continuous.
277
277
  */
278
- unsettledTail(e) {
279
- const t = this.stores[e]?.getAll();
280
- if (!t || t.length < 2) return [];
278
+ unsettledTail(t) {
279
+ const e = this.stores[t]?.getAll();
280
+ if (!e || e.length < 2) return [];
281
281
  const s = [];
282
- for (let i = t.length - 1; i >= 1; i--) {
283
- const r = this.entranceProgress(e, t[i].time);
284
- if (r >= 1) break;
285
- s.unshift({ time: t[i].time, progress: r });
282
+ for (let i = e.length - 1; i >= 1; i--) {
283
+ const n = this.entranceProgress(t, e[i].time);
284
+ if (n >= 1) break;
285
+ s.unshift({ time: e[i].time, progress: n });
286
286
  }
287
287
  return s;
288
288
  }
@@ -294,8 +294,8 @@ class F {
294
294
  * Falls back to `rawValue` when the store is empty or no smoothing has
295
295
  * happened yet.
296
296
  */
297
- effectiveValue(e, t, s, i) {
298
- return this.liveValue(t, s, i);
297
+ effectiveValue(t, e, s, i) {
298
+ return this.liveValue(e, s, i);
299
299
  }
300
300
  /**
301
301
  * The ctx-independent core of {@link effectiveValue} — used directly by the
@@ -304,14 +304,14 @@ class F {
304
304
  * transform). Those queries only need the live smoothing/chase this base
305
305
  * method already provides, never the intro's positional transform.
306
306
  */
307
- liveValue(e, t, s) {
308
- const i = this.#s[e]?.get(t);
307
+ liveValue(t, e, s) {
308
+ const i = this.#s[t]?.get(e);
309
309
  if (i !== void 0) return i.current;
310
- const r = this.stores[e]?.last()?.time;
311
- if (r !== void 0 && t === r)
312
- return this.displayedLastValues[e] ?? s;
313
- const n = this.#e[e];
314
- return n !== null && n.time === t ? n.anim.current : s;
310
+ const n = this.stores[t]?.last()?.time;
311
+ if (n !== void 0 && e === n)
312
+ return this.displayedLastValues[t] ?? s;
313
+ const r = this.#e[t];
314
+ return r !== null && r.time === e ? r.anim.current : s;
315
315
  }
316
316
  /**
317
317
  * Value a last-value/tooltip query should report for `time` — defaults to
@@ -320,8 +320,8 @@ class F {
320
320
  * override) without that lerp feeding back into `effectiveValue`, which
321
321
  * the renderer's own per-point draws use as an unlerp'd target.
322
322
  */
323
- snapshotValue(e, t, s) {
324
- return this.liveValue(e, t, s);
323
+ snapshotValue(t, e, s) {
324
+ return this.liveValue(t, e, s);
325
325
  }
326
326
  // --- Animation lifecycle --------------------------------------------------
327
327
  /**
@@ -330,44 +330,44 @@ class F {
330
330
  * subclass passes its own clock so test harnesses (which stub
331
331
  * `performance.now`) can drive progression deterministically.
332
332
  */
333
- tickAnimations(e) {
334
- this.introWave.tick(e), this.historyWave.tick(e);
335
- for (const t of this.#i) t.tick(e);
336
- for (let t = 0; t < this.stores.length; t++) {
337
- const s = this.#t[t];
333
+ tickAnimations(t) {
334
+ this.introWave.tick(t), this.historyWave.tick(t);
335
+ for (const e of this.#i) e.tick(t);
336
+ for (let e = 0; e < this.stores.length; e++) {
337
+ const s = this.#t[e];
338
338
  if (s !== null) {
339
- const l = s.tick(e);
340
- this.displayedLastValues[t] = s.current, l || (this.#t[t] = null);
339
+ const l = s.tick(t);
340
+ this.displayedLastValues[e] = s.current, l || (this.#t[e] = null);
341
341
  }
342
- const i = this.#e[t];
343
- i !== null && !i.anim.tick(e) && (this.#e[t] = null);
344
- const r = this.#s[t];
345
- for (const [l, u] of r)
346
- u.tick(e) || r.delete(l);
347
- const n = this.options.entryMs;
348
- if (n <= 0) {
349
- this.entries[t].clear();
342
+ const i = this.#e[e];
343
+ i !== null && !i.anim.tick(t) && (this.#e[e] = null);
344
+ const n = this.#s[e];
345
+ for (const [l, u] of n)
346
+ u.tick(t) || n.delete(l);
347
+ const r = this.options.entryMs;
348
+ if (r <= 0) {
349
+ this.entries[e].clear();
350
350
  continue;
351
351
  }
352
- const o = this.entries[t];
352
+ const o = this.entries[e];
353
353
  for (const [l, u] of o)
354
- e - u.startTime >= n && o.delete(l);
354
+ t - u.startTime >= r && o.delete(l);
355
355
  }
356
356
  }
357
357
  /** True while any layer has an active chase, unsettled entry, alpha fade,
358
358
  * or the initial-load reveal is still sweeping. */
359
359
  get needsAnimation() {
360
360
  if (this.introWave.active || this.historyWave.active) return !0;
361
- for (const e of this.#i)
362
- if (e.animating) return !0;
363
- for (const e of this.#t)
364
- if (e !== null) return !0;
365
- for (const e of this.#e)
366
- if (e !== null) return !0;
367
- for (const e of this.#s)
368
- if (e.size > 0) return !0;
369
- for (const e of this.entries)
370
- if (e.size > 0) return !0;
361
+ for (const t of this.#i)
362
+ if (t.animating) return !0;
363
+ for (const t of this.#t)
364
+ if (t !== null) return !0;
365
+ for (const t of this.#e)
366
+ if (t !== null) return !0;
367
+ for (const t of this.#s)
368
+ if (t.size > 0) return !0;
369
+ for (const t of this.entries)
370
+ if (t.size > 0) return !0;
371
371
  return !1;
372
372
  }
373
373
  /** Reveal-front position of the initial-load intro; `1` once settled. */
@@ -382,16 +382,16 @@ class F {
382
382
  */
383
383
  cancelEntranceAnimations() {
384
384
  this.introWave.finish();
385
- for (const e of this.entries) e.clear();
385
+ for (const t of this.entries) t.clear();
386
386
  }
387
387
  /**
388
388
  * Start a series-wide fade toward `target` over `durationMs`. Fans out
389
389
  * across every per-layer animator so a `setSeriesVisible` toggle is just
390
390
  * "set every layer to the same target". `durationMs <= 0` snaps.
391
391
  */
392
- setAlpha(e, t) {
392
+ setAlpha(t, e) {
393
393
  for (const s of this.#i)
394
- s.setTarget(e, { duration: t });
394
+ s.setTarget(t, { duration: e });
395
395
  }
396
396
  /**
397
397
  * `1` while any layer has alpha > 0 OR is animating, `0` when every layer
@@ -407,8 +407,8 @@ class F {
407
407
  * would deadlock at `0`.
408
408
  */
409
409
  getAlpha() {
410
- for (const e of this.#i)
411
- if (e.current > 0 || e.animating) return 1;
410
+ for (const t of this.#i)
411
+ if (t.current > 0 || t.animating) return 1;
412
412
  return 0;
413
413
  }
414
414
  /**
@@ -416,125 +416,174 @@ class F {
416
416
  * Subclass draw loops multiply this into `globalAlpha` per layer so the
417
417
  * fade lives next to the geometry it affects.
418
418
  */
419
- setLayerAlpha(e, t, s) {
420
- this.#i[e]?.setTarget(t, { duration: s });
419
+ setLayerAlpha(t, e, s) {
420
+ this.#i[t]?.setTarget(e, { duration: s });
421
421
  }
422
422
  /** Latest rendered per-layer alpha. Defaults to 1 for out-of-range indices. */
423
- getLayerAlpha(e) {
424
- return this.#i[e]?.current ?? 1;
423
+ getLayerAlpha(t) {
424
+ return this.#i[t]?.current ?? 1;
425
425
  }
426
426
  // --- Data queries ---------------------------------------------------------
427
+ /**
428
+ * The layer's newest point with a finite value, after skipping `skip` such
429
+ * points — a trailing `null` / `NaN` is a gap, not the latest reading.
430
+ */
431
+ lastFinitePoint(t, e = 0) {
432
+ const s = this.stores[t]?.getAll() ?? [];
433
+ let i = e;
434
+ for (let n = s.length - 1; n >= 0; n--)
435
+ if (Number.isFinite(s[n].value)) {
436
+ if (i === 0) return s[n];
437
+ i--;
438
+ }
439
+ return null;
440
+ }
427
441
  getLastValue() {
428
- for (let e = this.stores.length - 1; e >= 0; e--) {
429
- const t = this.stores[e].last();
430
- if (t) return t.value;
442
+ for (let t = this.stores.length - 1; t >= 0; t--) {
443
+ const e = this.lastFinitePoint(t);
444
+ if (e) return e.value;
431
445
  }
432
446
  return null;
433
447
  }
434
- getDataAtTime(e, t) {
435
- return this.stores[0]?.findNearest(e, t) ?? null;
448
+ getDataAtTime(t, e) {
449
+ return this.stores[0]?.findNearest(t, e) ?? null;
436
450
  }
437
- getLayerSnapshots(e, t) {
451
+ getLayerSnapshots(t, e) {
438
452
  if (this.stores.length <= 1) return null;
439
453
  const s = [];
440
- for (let i = 0; i < this.stores.length; i++) {
441
- if (!this.stores[i].isVisible()) continue;
442
- const r = this.stores[i].findNearest(e, t);
454
+ for (let n = 0; n < this.stores.length; n++) {
455
+ if (!this.stores[n].isVisible()) continue;
456
+ const r = this.stores[n].findNearest(t, e);
443
457
  if (r === null) continue;
444
- const n = {
445
- layerIndex: i,
458
+ const o = {
459
+ layerIndex: n,
446
460
  time: r.time,
447
461
  // Routes through the same live/pinned-chase smoothing the renderer
448
462
  // paints, so hovering the live edge mid-animation doesn't jump ahead
449
463
  // to the raw target before the on-screen marker gets there.
450
- value: this.snapshotValue(i, r.time, r.value),
464
+ value: this.snapshotValue(n, r.time, r.value),
451
465
  // Resolve the layer's color at the hovered datum's value so a value-fn
452
466
  // colors the tooltip dot to match the bar / segment under the cursor.
453
- color: this.resolveLayerColor(i, r.value)
467
+ color: this.resolveLayerColor(n, r.value)
454
468
  };
455
- r.x !== void 0 && (n.x = r.x), s.push(n);
469
+ r.x !== void 0 && (o.x = r.x), s.push(o);
456
470
  }
457
- return s.length > 0 ? s : null;
471
+ const i = this.withShares({ rows: s, keepGaps: !1 });
472
+ return i.length > 0 ? i : null;
458
473
  }
459
474
  getStackedLastValue() {
460
475
  if (this.stores.length <= 1) {
461
- const r = this.stores[0]?.last();
462
- return r ? { value: this.snapshotValue(0, r.time, r.value), isLive: !0 } : null;
476
+ const i = this.lastFinitePoint(0);
477
+ return i ? { value: this.snapshotValue(0, i.time, i.value), isLive: !0 } : null;
463
478
  }
464
- const e = this.options.stacking;
465
- if (e === "off") {
466
- for (let r = this.stores.length - 1; r >= 0; r--) {
467
- if (!this.stores[r].isVisible()) continue;
468
- const n = this.stores[r].last();
469
- if (n) return { value: this.snapshotValue(r, n.time, n.value), isLive: !0 };
479
+ const t = this.options.stacking;
480
+ if (t === "off") {
481
+ for (let i = this.stores.length - 1; i >= 0; i--) {
482
+ if (!this.stores[i].isVisible()) continue;
483
+ const n = this.lastFinitePoint(i);
484
+ if (n) return { value: this.snapshotValue(i, n.time, n.value), isLive: !0 };
470
485
  }
471
486
  return null;
472
487
  }
473
- let t = !1;
488
+ const e = this.stackHeadValues((i) => this.stores[i].last() ?? null) ?? this.lastDrawnRowValues();
489
+ if (!e) return null;
490
+ const s = w(e);
491
+ return { value: t === "percent" ? A(s) : k(s), isLive: !0 };
492
+ }
493
+ /** Stack contributions at the newest time any visible layer has a finite point. */
494
+ lastDrawnRowValues() {
495
+ let t = null;
496
+ for (let s = 0; s < this.stores.length; s++) {
497
+ if (!this.stores[s].isVisible()) continue;
498
+ const i = this.lastFinitePoint(s);
499
+ i !== null && (t === null || i.time > t) && (t = i.time);
500
+ }
501
+ if (t === null) return null;
502
+ const e = t;
503
+ return this.stackHeadValues((s) => this.stores[s].findNearest(e, 0));
504
+ }
505
+ /** Each visible layer's stack contribution at `pick(layer)`, or `null` when none is finite. */
506
+ stackHeadValues(t) {
507
+ let e = !1;
474
508
  const s = [];
475
- for (let r = 0; r < this.stores.length; r++) {
476
- if (!this.stores[r].isVisible()) continue;
477
- const n = this.stores[r].last();
478
- if (!n) {
509
+ for (let i = 0; i < this.stores.length; i++) {
510
+ if (!this.stores[i].isVisible()) continue;
511
+ const n = t(i);
512
+ if (!n || !Number.isFinite(n.value)) {
479
513
  s.push(0);
480
514
  continue;
481
515
  }
482
- t = !0, s.push(this.snapshotValue(r, n.time, n.value));
516
+ e = !0, s.push(this.snapshotValue(i, n.time, n.value));
483
517
  }
484
- if (!t) return null;
485
- const i = A(s);
486
- return { value: e === "percent" ? b(i) : w(i), isLive: !0 };
518
+ return e ? s : null;
487
519
  }
488
- getStackedValueAtTime(e, t) {
520
+ getStackedValueAtTime(t, e) {
489
521
  if (this.stores.length <= 1) {
490
- const o = this.stores[0]?.findNearest(e, t);
522
+ const o = this.stores[0]?.findNearest(t, e);
491
523
  return o ? this.snapshotValue(0, o.time, o.value) : null;
492
524
  }
493
525
  const s = this.options.stacking;
494
526
  if (s === "off") {
495
527
  for (let o = this.stores.length - 1; o >= 0; o--) {
496
528
  if (!this.stores[o].isVisible()) continue;
497
- const l = this.stores[o].findNearest(e, t);
529
+ const l = this.stores[o].findNearest(t, e);
498
530
  if (l) return this.snapshotValue(o, l.time, l.value);
499
531
  }
500
532
  return null;
501
533
  }
502
534
  let i = !1;
503
- const r = [];
535
+ const n = [];
504
536
  for (let o = 0; o < this.stores.length; o++) {
505
537
  if (!this.stores[o].isVisible()) continue;
506
- const l = this.stores[o].findNearest(e, t);
538
+ const l = this.stores[o].findNearest(t, e);
507
539
  if (!l) {
508
- r.push(0);
540
+ n.push(0);
509
541
  continue;
510
542
  }
511
- i = !0, r.push(this.snapshotValue(o, l.time, l.value));
543
+ i = !0, n.push(this.snapshotValue(o, l.time, l.value));
512
544
  }
513
545
  if (!i) return null;
514
- const n = A(r);
515
- return s === "percent" ? b(n) : w(n);
546
+ const r = w(n);
547
+ return s === "percent" ? A(r) : k(r);
516
548
  }
517
549
  getLayerLastSnapshots() {
518
550
  if (this.stores.length <= 1) return null;
519
- const e = [];
520
- for (let t = 0; t < this.stores.length; t++) {
521
- if (!this.stores[t].isVisible()) continue;
522
- const s = this.stores[t].last();
523
- if (!s) continue;
524
- const i = {
525
- layerIndex: t,
526
- time: s.time,
527
- value: this.snapshotValue(t, s.time, s.value),
528
- color: this.resolveLayerColor(t, s.value)
551
+ const t = [];
552
+ for (let s = 0; s < this.stores.length; s++) {
553
+ if (!this.stores[s].isVisible()) continue;
554
+ const i = this.lastFinitePoint(s);
555
+ if (!i) continue;
556
+ const n = {
557
+ layerIndex: s,
558
+ time: i.time,
559
+ value: this.snapshotValue(s, i.time, i.value),
560
+ color: this.resolveLayerColor(s, i.value)
529
561
  };
530
- s.x !== void 0 && (i.x = s.x), e.push(i);
562
+ i.x !== void 0 && (n.x = i.x), t.push(n);
531
563
  }
564
+ const e = this.withShares({ rows: t, keepGaps: !0 });
532
565
  return e.length > 0 ? e : null;
533
566
  }
567
+ /**
568
+ * On a percent stack, give each row its share of the column at the row's own
569
+ * time — ragged layers end at different columns. A row whose column is a gap
570
+ * is dropped, or kept without a share when `keepGaps`.
571
+ */
572
+ withShares(t) {
573
+ if (this.options.stacking !== "percent") return t.rows;
574
+ const e = /* @__PURE__ */ new Map(), s = [];
575
+ for (const i of t.rows) {
576
+ let n = e.get(i.time);
577
+ n === void 0 && (n = this.percentShares(i.time), e.set(i.time, n));
578
+ const r = n?.get(i.layerIndex);
579
+ r !== void 0 && (i.share = r), (r !== void 0 || t.keepGaps) && s.push(i);
580
+ }
581
+ return s;
582
+ }
534
583
  getTotalLength() {
535
- let e = 0;
536
- for (const t of this.stores) e += t.length;
537
- return e;
584
+ let t = 0;
585
+ for (const e of this.stores) t += e.length;
586
+ return t;
538
587
  }
539
588
  // --- TimeSeriesRenderer data queries --------------------------------------
540
589
  /** Earliest first-sample / latest last-sample across ALL layers, skipping
@@ -542,64 +591,63 @@ class F {
542
591
  * the multi-layer time-range leak: a populated layer 1 with an empty layer 0
543
592
  * still reports the real bounds. */
544
593
  getTimeBounds() {
545
- let e = 1 / 0, t = -1 / 0;
594
+ let t = 1 / 0, e = -1 / 0;
546
595
  for (const s of this.stores) {
547
596
  const i = s.first();
548
597
  if (!i) continue;
549
- const r = s.last() ?? i;
550
- i.time < e && (e = i.time), r.time > t && (t = r.time);
598
+ const n = s.last() ?? i;
599
+ i.time < t && (t = i.time), n.time > e && (e = n.time);
551
600
  }
552
- return e === 1 / 0 ? null : { first: e, last: t };
601
+ return t === 1 / 0 ? null : { first: t, last: e };
553
602
  }
554
603
  /**
555
- * Last data point of **layer 0 (`stores[0]`) only** — preserves the behavior
604
+ * Last finite data point of **layer 0 (`stores[0]`) only** — preserves the behavior
556
605
  * of the deleted `get store()` getter. NOTE: this is intentionally NOT the
557
606
  * same as {@link getStackedLastValue}, which for non-stacked multi-layer
558
607
  * reports the top visible layer and for stacked reports the cumulative sum.
559
608
  * Last-data / previous-close semantics stay pinned to layer 0 for back-compat.
560
609
  */
561
610
  getLastDataPoint() {
562
- return this.stores[0]?.last() ?? null;
611
+ return this.lastFinitePoint(0);
563
612
  }
564
613
  /** Second-to-last point of layer 0 (`stores[0]`). See {@link getLastDataPoint}. */
565
614
  getSecondLastDataPoint() {
566
- const e = this.stores[0]?.getAll();
567
- return !e || e.length < 2 ? null : e[e.length - 2];
568
- }
569
- /** First `maxCount` sample times from the first populated layer. */
570
- sampleTimes(e) {
571
- for (const t of this.stores) {
572
- const s = t.getAll();
573
- if (s.length !== 0)
574
- return s.slice(0, e).map((i) => i.time);
615
+ return this.lastFinitePoint(0, 1);
616
+ }
617
+ /** First (or with `'end'`, last) `maxCount` sample times from the first populated layer. */
618
+ sampleTimes(t, e = "start") {
619
+ for (const s of this.stores) {
620
+ const i = s.getAll();
621
+ if (i.length !== 0)
622
+ return M(i, { maxCount: t, from: e }).map((n) => n.time);
575
623
  }
576
624
  return [];
577
625
  }
578
626
  /** Visible points of layer 0 (`stores[0]`). See {@link getLastDataPoint}. */
579
- getVisibleDataPoints(e, t) {
580
- return this.stores[0]?.getVisibleData(e, t) ?? [];
627
+ getVisibleDataPoints(t, e) {
628
+ return this.stores[0]?.getVisibleData(t, e) ?? [];
581
629
  }
582
630
  get stacking() {
583
631
  return this.options.stacking;
584
632
  }
585
- getValueRange(e, t, s) {
633
+ getValueRange(t, e, s) {
586
634
  const i = this.options.stacking;
587
635
  if (i === "percent")
588
636
  return { min: 0, max: 100 };
589
- const r = this.stores.map((a) => a.isVisible() ? a.getVisibleData(e, t) : []), n = s?.plottable;
637
+ const n = this.stores.map((a) => a.isVisible() ? a.getVisibleData(t, e) : []), r = s?.plottable;
590
638
  if (i === "off") {
591
639
  let a = 1 / 0, c = -1 / 0;
592
- for (const f of r)
640
+ for (const f of n)
593
641
  for (const h of f)
594
- Number.isFinite(h.value) && (n && !n(h.value) || (h.value < a && (a = h.value), h.value > c && (c = h.value)));
642
+ Number.isFinite(h.value) && (r && !r(h.value) || (h.value < a && (a = h.value), h.value > c && (c = h.value)));
595
643
  return a < 1 / 0 ? { min: a, max: c } : null;
596
644
  }
597
- if (n) return S(r, n);
645
+ if (r) return T(n, r);
598
646
  const o = /* @__PURE__ */ new Map();
599
- for (let a = 0; a < r.length; a++)
600
- for (const c of r[a]) {
647
+ for (let a = 0; a < n.length; a++)
648
+ for (const c of n[a]) {
601
649
  let f = o.get(c.time);
602
- f || (f = new Array(r.length).fill(0), o.set(c.time, f)), f[a] = Number.isFinite(c.value) ? c.value : 0;
650
+ f || (f = new Array(n.length).fill(0), o.set(c.time, f)), f[a] = Number.isFinite(c.value) ? c.value : 0;
603
651
  }
604
652
  let l = 0, u = 0;
605
653
  for (const a of o.values()) {
@@ -611,26 +659,26 @@ class F {
611
659
  return u > l ? { min: l, max: u } : null;
612
660
  }
613
661
  }
614
- function S(m, e) {
615
- const t = /* @__PURE__ */ new Map();
616
- for (let n = 0; n < m.length; n++)
617
- for (const o of m[n]) {
618
- let l = t.get(o.time);
619
- l || (l = new Array(m.length).fill(0), t.set(o.time, l)), l[n] = e(o.value) ? o.value : 0;
662
+ function T(m, t) {
663
+ const e = /* @__PURE__ */ new Map();
664
+ for (let r = 0; r < m.length; r++)
665
+ for (const o of m[r]) {
666
+ let l = e.get(o.time);
667
+ l || (l = new Array(m.length).fill(0), e.set(o.time, l)), l[r] = t(o.value) ? o.value : 0;
620
668
  }
621
- const s = e(0);
622
- let i = s ? 0 : 1 / 0, r = s ? 0 : -1 / 0;
623
- for (const n of t.values()) {
669
+ const s = t(0);
670
+ let i = s ? 0 : 1 / 0, n = s ? 0 : -1 / 0;
671
+ for (const r of e.values()) {
624
672
  let o = 0, l = 0;
625
- for (const u of n) {
673
+ for (const u of r) {
626
674
  if (u === 0) continue;
627
675
  u > 0 ? o += u : l += u;
628
676
  const a = u > 0 ? o : l;
629
- e(a) && (a < i && (i = a), a > r && (r = a));
677
+ t(a) && (a < i && (i = a), a > n && (n = a));
630
678
  }
631
679
  }
632
- return i < 1 / 0 ? { min: i, max: r } : null;
680
+ return i < 1 / 0 ? { min: i, max: n } : null;
633
681
  }
634
682
  export {
635
- F as BaseMultiLayerSeries
683
+ I as BaseMultiLayerSeries
636
684
  };