@wick-charts/vue 0.8.1 → 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.
- package/dist/BandSeries.vue.js +25 -24
- package/dist/BarSeries.vue.js +27 -26
- package/dist/CandlestickSeries.vue.js +25 -24
- package/dist/HeatmapSeries.vue.js +21 -20
- package/dist/LineSeries.vue.js +24 -23
- package/dist/PieSeries.vue.js +25 -24
- package/dist/composables.js +63 -36
- package/dist/core/dist/axis/dom-labels.js +64 -53
- package/dist/core/dist/chart/options.js +1 -1
- package/dist/core/dist/chart/y-target.js +38 -32
- package/dist/core/dist/chart.js +475 -439
- package/dist/core/dist/data/decimation.js +52 -51
- package/dist/core/dist/data/store.js +38 -20
- package/dist/core/dist/data/sync.js +80 -69
- package/dist/core/dist/perf/perf-hud.js +3 -3
- package/dist/core/dist/scales/tick-tracker.js +2 -1
- package/dist/core/dist/series/band.js +1 -1
- package/dist/core/dist/series/bar.js +240 -211
- package/dist/core/dist/series/base-multi-layer.js +267 -249
- package/dist/core/dist/series/candlestick.js +116 -116
- package/dist/core/dist/series/line.js +540 -455
- package/dist/core/dist/snapshots.js +96 -77
- package/dist/index.d.ts +22 -4
- package/dist/ui/InfoBar.vue.js +68 -68
- package/dist/ui/NumberFlow.vue.js +36 -27
- package/dist/ui/Tooltip.vue.js +127 -130
- package/package.json +2 -2
- package/src/BandSeries.vue +5 -3
- package/src/BarSeries.vue +5 -3
- package/src/CandlestickSeries.vue +5 -4
- package/src/ChartContainer.vue +1 -1
- package/src/HeatmapSeries.vue +5 -4
- package/src/LineSeries.vue +5 -3
- package/src/PieSeries.vue +6 -5
- package/src/composables.ts +64 -1
- package/src/ui/InfoBar.vue +11 -3
- package/src/ui/NumberFlow.vue +17 -3
- package/src/ui/Tooltip.vue +23 -12
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { Animator as
|
|
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
4
|
import { TimeSeriesStore as P } from "../data/store.js";
|
|
5
|
-
import { resolveColor as
|
|
6
|
-
import { normalizeTimePointArray as d, normalizeTime as L, sliceSample as
|
|
7
|
-
import { sumStack as
|
|
8
|
-
const
|
|
9
|
-
class
|
|
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 z {
|
|
|
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(
|
|
57
|
-
this.stores = Array.from({ length:
|
|
58
|
-
{ length:
|
|
59
|
-
() => new
|
|
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 ------------------------------------------------------
|
|
@@ -66,15 +66,15 @@ class z {
|
|
|
66
66
|
* a theme swap, which rewrites `options.colors`, leaves these untouched.
|
|
67
67
|
*/
|
|
68
68
|
#n = [];
|
|
69
|
-
setColorOverrides(
|
|
70
|
-
this.#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
|
-
#r(
|
|
74
|
-
const
|
|
75
|
-
if (
|
|
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[
|
|
77
|
+
return s[t % s.length];
|
|
78
78
|
}
|
|
79
79
|
/** The first layer's color resolver (string or value-fn). */
|
|
80
80
|
getColor() {
|
|
@@ -84,71 +84,71 @@ class z {
|
|
|
84
84
|
return this.options.colors;
|
|
85
85
|
}
|
|
86
86
|
/** Resolve one layer's color for a given datum value. */
|
|
87
|
-
resolveLayerColor(
|
|
88
|
-
return
|
|
87
|
+
resolveLayerColor(t, e) {
|
|
88
|
+
return S(this.#r(t), e);
|
|
89
89
|
}
|
|
90
90
|
// --- Data ingest ----------------------------------------------------------
|
|
91
|
-
setData(
|
|
92
|
-
const s = this.stores[
|
|
91
|
+
setData(t, e = 0) {
|
|
92
|
+
const s = this.stores[e];
|
|
93
93
|
if (!s) return;
|
|
94
94
|
let i = !1;
|
|
95
|
-
for (const
|
|
96
|
-
|
|
97
|
-
const n = d(
|
|
98
|
-
|
|
99
|
-
const
|
|
100
|
-
this.displayedLastValues[
|
|
95
|
+
for (const o of this.stores)
|
|
96
|
+
o.length > 0 && (i = !0);
|
|
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(
|
|
108
|
-
const s = this.stores[
|
|
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(
|
|
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(
|
|
117
|
-
const n = d(
|
|
116
|
+
i.set(h.time, this.liveValue(e, h.time, h.value));
|
|
117
|
+
const n = d(t ?? []);
|
|
118
118
|
s.setData(n);
|
|
119
|
-
const
|
|
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
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 ||
|
|
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:
|
|
129
|
+
v.target !== p && v.retarget(p, { settleMs: r }), a.set(h.time, v);
|
|
130
130
|
}
|
|
131
|
-
const
|
|
132
|
-
for (const h of
|
|
133
|
-
|
|
134
|
-
this.#s[
|
|
135
|
-
const
|
|
136
|
-
this.displayedLastValues[
|
|
137
|
-
}
|
|
138
|
-
appendPoint(
|
|
139
|
-
const s = this.stores[
|
|
131
|
+
const c = new Set(n.map((h) => h.time));
|
|
132
|
+
for (const h of l.keys())
|
|
133
|
+
c.has(h) || l.delete(h);
|
|
134
|
+
this.#s[e] = a;
|
|
135
|
+
const f = s.last();
|
|
136
|
+
this.displayedLastValues[e] = f ? f.value : null, this.#t[e] = null, this.#e[e] = null;
|
|
137
|
+
}
|
|
138
|
+
appendPoint(t, e = 0) {
|
|
139
|
+
const s = this.stores[e];
|
|
140
140
|
if (!s) return;
|
|
141
|
-
const i =
|
|
141
|
+
const i = t, n = L(i.time), r = s.last(), o = s.length;
|
|
142
142
|
s.append({ ...i, time: n });
|
|
143
|
-
const
|
|
144
|
-
|
|
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[
|
|
146
|
+
this.isEntryEnabled() && u > 0 && this.entries[e].set(n, { startTime: performance.now() });
|
|
147
147
|
}
|
|
148
|
-
prependPoints(
|
|
149
|
-
const s = this.stores[
|
|
150
|
-
if (!s ||
|
|
151
|
-
const i = d(
|
|
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
152
|
s.prepend(i), this.#o(i[0]?.time, n);
|
|
153
153
|
}
|
|
154
154
|
/**
|
|
@@ -157,71 +157,71 @@ class z {
|
|
|
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(
|
|
160
|
+
#o(t, e) {
|
|
161
161
|
const s = this.options.historyRevealMs;
|
|
162
|
-
if (!(this.options.historyReveal === "none" || s <= 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,
|
|
164
|
+
this.historyRange = { from: Math.min(this.historyRange.from, t), to: this.historyRange.to };
|
|
165
165
|
return;
|
|
166
166
|
}
|
|
167
|
-
this.historyRange = { from:
|
|
167
|
+
this.historyRange = { from: t, to: e }, this.historyWave.arm(s);
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
|
-
updateLastPoint(
|
|
171
|
-
const s = this.stores[
|
|
170
|
+
updateLastPoint(t, e = 0) {
|
|
171
|
+
const s = this.stores[e];
|
|
172
172
|
if (!s) return;
|
|
173
|
-
const i =
|
|
173
|
+
const i = t, n = s.last()?.time;
|
|
174
174
|
s.updateLast({ ...i, time: L(i.time) });
|
|
175
|
-
const
|
|
176
|
-
n !== void 0 &&
|
|
177
|
-
const
|
|
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
|
+
const l = i.value, u = this.options.smoothMs;
|
|
178
178
|
if (u <= 0) {
|
|
179
|
-
this.displayedLastValues[
|
|
179
|
+
this.displayedLastValues[e] = l, this.#t[e] = null;
|
|
180
180
|
return;
|
|
181
181
|
}
|
|
182
|
-
let a = this.#t[
|
|
182
|
+
let a = this.#t[e];
|
|
183
183
|
if (a === null) {
|
|
184
|
-
const
|
|
185
|
-
a = new V(
|
|
184
|
+
const c = this.displayedLastValues[e] ?? l;
|
|
185
|
+
a = new V(c), this.#t[e] = a;
|
|
186
186
|
}
|
|
187
|
-
a.retarget(
|
|
187
|
+
a.retarget(l, { settleMs: u });
|
|
188
188
|
}
|
|
189
|
-
keepLast(
|
|
190
|
-
const s = this.stores[
|
|
191
|
-
if (!s ||
|
|
192
|
-
const i = s.length -
|
|
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(
|
|
200
|
-
this.stores[
|
|
199
|
+
setLayerVisible(t, e) {
|
|
200
|
+
this.stores[t]?.setVisible(e);
|
|
201
201
|
}
|
|
202
|
-
isLayerVisible(
|
|
203
|
-
return this.stores[
|
|
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
|
|
210
|
-
for (let
|
|
211
|
-
const s = this.lastFinitePoint(
|
|
212
|
-
|
|
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
|
|
214
|
+
return t;
|
|
215
215
|
}
|
|
216
216
|
// --- Lifecycle ------------------------------------------------------------
|
|
217
|
-
onDataChanged(
|
|
218
|
-
for (const
|
|
217
|
+
onDataChanged(t) {
|
|
218
|
+
for (const e of this.stores) e.on("update", t);
|
|
219
219
|
return () => {
|
|
220
|
-
for (const
|
|
220
|
+
for (const e of this.stores) e.off("update", t);
|
|
221
221
|
};
|
|
222
222
|
}
|
|
223
223
|
dispose() {
|
|
224
|
-
for (const
|
|
224
|
+
for (const t of this.stores) t.removeAllListeners();
|
|
225
225
|
}
|
|
226
226
|
// --- Animation primitives — renderer-owned state --------------------------
|
|
227
227
|
/**
|
|
@@ -231,8 +231,8 @@ class z {
|
|
|
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(
|
|
235
|
-
const s = this.entries[
|
|
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;
|
|
@@ -247,9 +247,9 @@ class z {
|
|
|
247
247
|
* (bar) fold this into their {@link entranceProgress} override; the line
|
|
248
248
|
* renders its own continuous sweep instead.
|
|
249
249
|
*/
|
|
250
|
-
introProgressAt(
|
|
250
|
+
introProgressAt(t, e) {
|
|
251
251
|
if (!this.introWave.active) return 1;
|
|
252
|
-
const s =
|
|
252
|
+
const s = t.timeScale.getRange(), i = s.to - s.from, n = i > 0 ? (e - s.from) / i : 0;
|
|
253
253
|
return this.introWave.progressAt(n);
|
|
254
254
|
}
|
|
255
255
|
/**
|
|
@@ -258,10 +258,10 @@ class z {
|
|
|
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(
|
|
262
|
-
const
|
|
263
|
-
if (!this.historyWave.active ||
|
|
264
|
-
const s =
|
|
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 z {
|
|
|
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(
|
|
279
|
-
const
|
|
280
|
-
if (!
|
|
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 =
|
|
283
|
-
const n = this.entranceProgress(
|
|
282
|
+
for (let i = e.length - 1; i >= 1; i--) {
|
|
283
|
+
const n = this.entranceProgress(t, e[i].time);
|
|
284
284
|
if (n >= 1) break;
|
|
285
|
-
s.unshift({ time:
|
|
285
|
+
s.unshift({ time: e[i].time, progress: n });
|
|
286
286
|
}
|
|
287
287
|
return s;
|
|
288
288
|
}
|
|
@@ -294,8 +294,8 @@ class z {
|
|
|
294
294
|
* Falls back to `rawValue` when the store is empty or no smoothing has
|
|
295
295
|
* happened yet.
|
|
296
296
|
*/
|
|
297
|
-
effectiveValue(
|
|
298
|
-
return this.liveValue(
|
|
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 z {
|
|
|
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(
|
|
308
|
-
const i = this.#s[
|
|
307
|
+
liveValue(t, e, s) {
|
|
308
|
+
const i = this.#s[t]?.get(e);
|
|
309
309
|
if (i !== void 0) return i.current;
|
|
310
|
-
const n = this.stores[
|
|
311
|
-
if (n !== void 0 &&
|
|
312
|
-
return this.displayedLastValues[
|
|
313
|
-
const
|
|
314
|
-
return
|
|
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 z {
|
|
|
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(
|
|
324
|
-
return this.liveValue(
|
|
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 z {
|
|
|
330
330
|
* subclass passes its own clock so test harnesses (which stub
|
|
331
331
|
* `performance.now`) can drive progression deterministically.
|
|
332
332
|
*/
|
|
333
|
-
tickAnimations(
|
|
334
|
-
this.introWave.tick(
|
|
335
|
-
for (const
|
|
336
|
-
for (let
|
|
337
|
-
const s = this.#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
|
|
340
|
-
this.displayedLastValues[
|
|
339
|
+
const l = s.tick(t);
|
|
340
|
+
this.displayedLastValues[e] = s.current, l || (this.#t[e] = null);
|
|
341
341
|
}
|
|
342
|
-
const i = this.#e[
|
|
343
|
-
i !== null && !i.anim.tick(
|
|
344
|
-
const n = this.#s[
|
|
345
|
-
for (const [
|
|
346
|
-
u.tick(
|
|
347
|
-
const
|
|
348
|
-
if (
|
|
349
|
-
this.entries[
|
|
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
|
|
353
|
-
for (const [
|
|
354
|
-
|
|
352
|
+
const o = this.entries[e];
|
|
353
|
+
for (const [l, u] of o)
|
|
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
|
|
362
|
-
if (
|
|
363
|
-
for (const
|
|
364
|
-
if (
|
|
365
|
-
for (const
|
|
366
|
-
if (
|
|
367
|
-
for (const
|
|
368
|
-
if (
|
|
369
|
-
for (const
|
|
370
|
-
if (
|
|
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 z {
|
|
|
382
382
|
*/
|
|
383
383
|
cancelEntranceAnimations() {
|
|
384
384
|
this.introWave.finish();
|
|
385
|
-
for (const
|
|
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(
|
|
392
|
+
setAlpha(t, e) {
|
|
393
393
|
for (const s of this.#i)
|
|
394
|
-
s.setTarget(
|
|
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 z {
|
|
|
407
407
|
* would deadlock at `0`.
|
|
408
408
|
*/
|
|
409
409
|
getAlpha() {
|
|
410
|
-
for (const
|
|
411
|
-
if (
|
|
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,21 +416,21 @@ class z {
|
|
|
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(
|
|
420
|
-
this.#i[
|
|
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(
|
|
424
|
-
return this.#i[
|
|
423
|
+
getLayerAlpha(t) {
|
|
424
|
+
return this.#i[t]?.current ?? 1;
|
|
425
425
|
}
|
|
426
426
|
// --- Data queries ---------------------------------------------------------
|
|
427
427
|
/**
|
|
428
428
|
* The layer's newest point with a finite value, after skipping `skip` such
|
|
429
429
|
* points — a trailing `null` / `NaN` is a gap, not the latest reading.
|
|
430
430
|
*/
|
|
431
|
-
lastFinitePoint(
|
|
432
|
-
const s = this.stores[
|
|
433
|
-
let i =
|
|
431
|
+
lastFinitePoint(t, e = 0) {
|
|
432
|
+
const s = this.stores[t]?.getAll() ?? [];
|
|
433
|
+
let i = e;
|
|
434
434
|
for (let n = s.length - 1; n >= 0; n--)
|
|
435
435
|
if (Number.isFinite(s[n].value)) {
|
|
436
436
|
if (i === 0) return s[n];
|
|
@@ -439,44 +439,45 @@ class z {
|
|
|
439
439
|
return null;
|
|
440
440
|
}
|
|
441
441
|
getLastValue() {
|
|
442
|
-
for (let
|
|
443
|
-
const
|
|
444
|
-
if (
|
|
442
|
+
for (let t = this.stores.length - 1; t >= 0; t--) {
|
|
443
|
+
const e = this.lastFinitePoint(t);
|
|
444
|
+
if (e) return e.value;
|
|
445
445
|
}
|
|
446
446
|
return null;
|
|
447
447
|
}
|
|
448
|
-
getDataAtTime(
|
|
449
|
-
return this.stores[0]?.findNearest(
|
|
448
|
+
getDataAtTime(t, e) {
|
|
449
|
+
return this.stores[0]?.findNearest(t, e) ?? null;
|
|
450
450
|
}
|
|
451
|
-
getLayerSnapshots(
|
|
451
|
+
getLayerSnapshots(t, e) {
|
|
452
452
|
if (this.stores.length <= 1) return null;
|
|
453
453
|
const s = [];
|
|
454
|
-
for (let
|
|
455
|
-
if (!this.stores[
|
|
456
|
-
const
|
|
457
|
-
if (
|
|
458
|
-
const
|
|
459
|
-
layerIndex:
|
|
460
|
-
time:
|
|
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);
|
|
457
|
+
if (r === null) continue;
|
|
458
|
+
const o = {
|
|
459
|
+
layerIndex: n,
|
|
460
|
+
time: r.time,
|
|
461
461
|
// Routes through the same live/pinned-chase smoothing the renderer
|
|
462
462
|
// paints, so hovering the live edge mid-animation doesn't jump ahead
|
|
463
463
|
// to the raw target before the on-screen marker gets there.
|
|
464
|
-
value: this.snapshotValue(
|
|
464
|
+
value: this.snapshotValue(n, r.time, r.value),
|
|
465
465
|
// Resolve the layer's color at the hovered datum's value so a value-fn
|
|
466
466
|
// colors the tooltip dot to match the bar / segment under the cursor.
|
|
467
|
-
color: this.resolveLayerColor(
|
|
467
|
+
color: this.resolveLayerColor(n, r.value)
|
|
468
468
|
};
|
|
469
|
-
|
|
469
|
+
r.x !== void 0 && (o.x = r.x), s.push(o);
|
|
470
470
|
}
|
|
471
|
-
|
|
471
|
+
const i = this.withShares({ rows: s, keepGaps: !1 });
|
|
472
|
+
return i.length > 0 ? i : null;
|
|
472
473
|
}
|
|
473
474
|
getStackedLastValue() {
|
|
474
475
|
if (this.stores.length <= 1) {
|
|
475
476
|
const i = this.lastFinitePoint(0);
|
|
476
477
|
return i ? { value: this.snapshotValue(0, i.time, i.value), isLive: !0 } : null;
|
|
477
478
|
}
|
|
478
|
-
const
|
|
479
|
-
if (
|
|
479
|
+
const t = this.options.stacking;
|
|
480
|
+
if (t === "off") {
|
|
480
481
|
for (let i = this.stores.length - 1; i >= 0; i--) {
|
|
481
482
|
if (!this.stores[i].isVisible()) continue;
|
|
482
483
|
const n = this.lastFinitePoint(i);
|
|
@@ -484,88 +485,105 @@ class z {
|
|
|
484
485
|
}
|
|
485
486
|
return null;
|
|
486
487
|
}
|
|
487
|
-
const
|
|
488
|
-
if (!
|
|
489
|
-
const s =
|
|
490
|
-
return { value:
|
|
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 };
|
|
491
492
|
}
|
|
492
493
|
/** Stack contributions at the newest time any visible layer has a finite point. */
|
|
493
494
|
lastDrawnRowValues() {
|
|
494
|
-
let
|
|
495
|
+
let t = null;
|
|
495
496
|
for (let s = 0; s < this.stores.length; s++) {
|
|
496
497
|
if (!this.stores[s].isVisible()) continue;
|
|
497
498
|
const i = this.lastFinitePoint(s);
|
|
498
|
-
i !== null && (
|
|
499
|
+
i !== null && (t === null || i.time > t) && (t = i.time);
|
|
499
500
|
}
|
|
500
|
-
if (
|
|
501
|
-
const
|
|
502
|
-
return this.stackHeadValues((s) => this.stores[s].findNearest(
|
|
501
|
+
if (t === null) return null;
|
|
502
|
+
const e = t;
|
|
503
|
+
return this.stackHeadValues((s) => this.stores[s].findNearest(e, 0));
|
|
503
504
|
}
|
|
504
505
|
/** Each visible layer's stack contribution at `pick(layer)`, or `null` when none is finite. */
|
|
505
|
-
stackHeadValues(
|
|
506
|
-
let
|
|
506
|
+
stackHeadValues(t) {
|
|
507
|
+
let e = !1;
|
|
507
508
|
const s = [];
|
|
508
509
|
for (let i = 0; i < this.stores.length; i++) {
|
|
509
510
|
if (!this.stores[i].isVisible()) continue;
|
|
510
|
-
const n =
|
|
511
|
+
const n = t(i);
|
|
511
512
|
if (!n || !Number.isFinite(n.value)) {
|
|
512
513
|
s.push(0);
|
|
513
514
|
continue;
|
|
514
515
|
}
|
|
515
|
-
|
|
516
|
+
e = !0, s.push(this.snapshotValue(i, n.time, n.value));
|
|
516
517
|
}
|
|
517
|
-
return
|
|
518
|
+
return e ? s : null;
|
|
518
519
|
}
|
|
519
|
-
getStackedValueAtTime(
|
|
520
|
+
getStackedValueAtTime(t, e) {
|
|
520
521
|
if (this.stores.length <= 1) {
|
|
521
|
-
const
|
|
522
|
-
return
|
|
522
|
+
const o = this.stores[0]?.findNearest(t, e);
|
|
523
|
+
return o ? this.snapshotValue(0, o.time, o.value) : null;
|
|
523
524
|
}
|
|
524
525
|
const s = this.options.stacking;
|
|
525
526
|
if (s === "off") {
|
|
526
|
-
for (let
|
|
527
|
-
if (!this.stores[
|
|
528
|
-
const
|
|
529
|
-
if (
|
|
527
|
+
for (let o = this.stores.length - 1; o >= 0; o--) {
|
|
528
|
+
if (!this.stores[o].isVisible()) continue;
|
|
529
|
+
const l = this.stores[o].findNearest(t, e);
|
|
530
|
+
if (l) return this.snapshotValue(o, l.time, l.value);
|
|
530
531
|
}
|
|
531
532
|
return null;
|
|
532
533
|
}
|
|
533
534
|
let i = !1;
|
|
534
535
|
const n = [];
|
|
535
|
-
for (let
|
|
536
|
-
if (!this.stores[
|
|
537
|
-
const
|
|
538
|
-
if (!
|
|
536
|
+
for (let o = 0; o < this.stores.length; o++) {
|
|
537
|
+
if (!this.stores[o].isVisible()) continue;
|
|
538
|
+
const l = this.stores[o].findNearest(t, e);
|
|
539
|
+
if (!l) {
|
|
539
540
|
n.push(0);
|
|
540
541
|
continue;
|
|
541
542
|
}
|
|
542
|
-
i = !0, n.push(this.snapshotValue(
|
|
543
|
+
i = !0, n.push(this.snapshotValue(o, l.time, l.value));
|
|
543
544
|
}
|
|
544
545
|
if (!i) return null;
|
|
545
|
-
const
|
|
546
|
-
return s === "percent" ?
|
|
546
|
+
const r = w(n);
|
|
547
|
+
return s === "percent" ? A(r) : k(r);
|
|
547
548
|
}
|
|
548
549
|
getLayerLastSnapshots() {
|
|
549
550
|
if (this.stores.length <= 1) return null;
|
|
550
|
-
const
|
|
551
|
-
for (let
|
|
552
|
-
if (!this.stores[
|
|
553
|
-
const
|
|
554
|
-
if (!
|
|
555
|
-
const
|
|
556
|
-
layerIndex:
|
|
557
|
-
time:
|
|
558
|
-
value: this.snapshotValue(
|
|
559
|
-
color: this.resolveLayerColor(
|
|
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)
|
|
560
561
|
};
|
|
561
|
-
|
|
562
|
+
i.x !== void 0 && (n.x = i.x), t.push(n);
|
|
562
563
|
}
|
|
564
|
+
const e = this.withShares({ rows: t, keepGaps: !0 });
|
|
563
565
|
return e.length > 0 ? e : null;
|
|
564
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
|
+
}
|
|
565
583
|
getTotalLength() {
|
|
566
|
-
let
|
|
567
|
-
for (const
|
|
568
|
-
return
|
|
584
|
+
let t = 0;
|
|
585
|
+
for (const e of this.stores) t += e.length;
|
|
586
|
+
return t;
|
|
569
587
|
}
|
|
570
588
|
// --- TimeSeriesRenderer data queries --------------------------------------
|
|
571
589
|
/** Earliest first-sample / latest last-sample across ALL layers, skipping
|
|
@@ -573,14 +591,14 @@ class z {
|
|
|
573
591
|
* the multi-layer time-range leak: a populated layer 1 with an empty layer 0
|
|
574
592
|
* still reports the real bounds. */
|
|
575
593
|
getTimeBounds() {
|
|
576
|
-
let
|
|
594
|
+
let t = 1 / 0, e = -1 / 0;
|
|
577
595
|
for (const s of this.stores) {
|
|
578
596
|
const i = s.first();
|
|
579
597
|
if (!i) continue;
|
|
580
598
|
const n = s.last() ?? i;
|
|
581
|
-
i.time <
|
|
599
|
+
i.time < t && (t = i.time), n.time > e && (e = n.time);
|
|
582
600
|
}
|
|
583
|
-
return
|
|
601
|
+
return t === 1 / 0 ? null : { first: t, last: e };
|
|
584
602
|
}
|
|
585
603
|
/**
|
|
586
604
|
* Last finite data point of **layer 0 (`stores[0]`) only** — preserves the behavior
|
|
@@ -597,70 +615,70 @@ class z {
|
|
|
597
615
|
return this.lastFinitePoint(0, 1);
|
|
598
616
|
}
|
|
599
617
|
/** First (or with `'end'`, last) `maxCount` sample times from the first populated layer. */
|
|
600
|
-
sampleTimes(
|
|
618
|
+
sampleTimes(t, e = "start") {
|
|
601
619
|
for (const s of this.stores) {
|
|
602
620
|
const i = s.getAll();
|
|
603
621
|
if (i.length !== 0)
|
|
604
|
-
return
|
|
622
|
+
return M(i, { maxCount: t, from: e }).map((n) => n.time);
|
|
605
623
|
}
|
|
606
624
|
return [];
|
|
607
625
|
}
|
|
608
626
|
/** Visible points of layer 0 (`stores[0]`). See {@link getLastDataPoint}. */
|
|
609
|
-
getVisibleDataPoints(
|
|
610
|
-
return this.stores[0]?.getVisibleData(
|
|
627
|
+
getVisibleDataPoints(t, e) {
|
|
628
|
+
return this.stores[0]?.getVisibleData(t, e) ?? [];
|
|
611
629
|
}
|
|
612
630
|
get stacking() {
|
|
613
631
|
return this.options.stacking;
|
|
614
632
|
}
|
|
615
|
-
getValueRange(
|
|
633
|
+
getValueRange(t, e, s) {
|
|
616
634
|
const i = this.options.stacking;
|
|
617
635
|
if (i === "percent")
|
|
618
636
|
return { min: 0, max: 100 };
|
|
619
|
-
const n = this.stores.map((a) => a.isVisible() ? a.getVisibleData(
|
|
637
|
+
const n = this.stores.map((a) => a.isVisible() ? a.getVisibleData(t, e) : []), r = s?.plottable;
|
|
620
638
|
if (i === "off") {
|
|
621
|
-
let a = 1 / 0,
|
|
622
|
-
for (const
|
|
623
|
-
for (const h of
|
|
624
|
-
Number.isFinite(h.value) && (
|
|
625
|
-
return a < 1 / 0 ? { min: a, max:
|
|
639
|
+
let a = 1 / 0, c = -1 / 0;
|
|
640
|
+
for (const f of n)
|
|
641
|
+
for (const h of f)
|
|
642
|
+
Number.isFinite(h.value) && (r && !r(h.value) || (h.value < a && (a = h.value), h.value > c && (c = h.value)));
|
|
643
|
+
return a < 1 / 0 ? { min: a, max: c } : null;
|
|
626
644
|
}
|
|
627
|
-
if (
|
|
628
|
-
const
|
|
645
|
+
if (r) return T(n, r);
|
|
646
|
+
const o = /* @__PURE__ */ new Map();
|
|
629
647
|
for (let a = 0; a < n.length; a++)
|
|
630
|
-
for (const
|
|
631
|
-
let
|
|
632
|
-
|
|
648
|
+
for (const c of n[a]) {
|
|
649
|
+
let f = o.get(c.time);
|
|
650
|
+
f || (f = new Array(n.length).fill(0), o.set(c.time, f)), f[a] = Number.isFinite(c.value) ? c.value : 0;
|
|
633
651
|
}
|
|
634
|
-
let
|
|
635
|
-
for (const a of
|
|
636
|
-
let
|
|
652
|
+
let l = 0, u = 0;
|
|
653
|
+
for (const a of o.values()) {
|
|
654
|
+
let c = 0, f = 0;
|
|
637
655
|
for (const h of a)
|
|
638
|
-
h > 0 ?
|
|
639
|
-
|
|
656
|
+
h > 0 ? c += h : f += h;
|
|
657
|
+
c > u && (u = c), f < l && (l = f);
|
|
640
658
|
}
|
|
641
|
-
return u >
|
|
659
|
+
return u > l ? { min: l, max: u } : null;
|
|
642
660
|
}
|
|
643
661
|
}
|
|
644
|
-
function T(m,
|
|
645
|
-
const
|
|
646
|
-
for (let
|
|
647
|
-
for (const
|
|
648
|
-
let
|
|
649
|
-
|
|
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;
|
|
650
668
|
}
|
|
651
|
-
const s =
|
|
669
|
+
const s = t(0);
|
|
652
670
|
let i = s ? 0 : 1 / 0, n = s ? 0 : -1 / 0;
|
|
653
|
-
for (const
|
|
654
|
-
let
|
|
655
|
-
for (const u of
|
|
671
|
+
for (const r of e.values()) {
|
|
672
|
+
let o = 0, l = 0;
|
|
673
|
+
for (const u of r) {
|
|
656
674
|
if (u === 0) continue;
|
|
657
|
-
u > 0 ?
|
|
658
|
-
const a = u > 0 ?
|
|
659
|
-
|
|
675
|
+
u > 0 ? o += u : l += u;
|
|
676
|
+
const a = u > 0 ? o : l;
|
|
677
|
+
t(a) && (a < i && (i = a), a > n && (n = a));
|
|
660
678
|
}
|
|
661
679
|
}
|
|
662
680
|
return i < 1 / 0 ? { min: i, max: n } : null;
|
|
663
681
|
}
|
|
664
682
|
export {
|
|
665
|
-
|
|
683
|
+
I as BaseMultiLayerSeries
|
|
666
684
|
};
|