@wick-charts/vue 0.5.0 → 0.5.1
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/ChartContainer.vue.js +11 -11
- package/dist/core/dist/chart.js +569 -514
- package/dist/core/dist/perf/perf-hud.js +7 -7
- package/dist/index.d.ts +7 -0
- package/package.json +2 -2
package/dist/core/dist/chart.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { AnimationConfig as z, DEFAULT_LINE_PULSE as
|
|
2
|
-
import { createViewportEngine as
|
|
1
|
+
import { AnimationConfig as z, DEFAULT_LINE_PULSE as N } from "./animation/config.js";
|
|
2
|
+
import { createViewportEngine as H } from "./animation/viewport-engine.js";
|
|
3
3
|
import { CanvasManager as W } from "./canvas-manager.js";
|
|
4
|
-
import { drawEdgeIndicators as E, resolveEdgeBoundary as
|
|
4
|
+
import { drawEdgeIndicators as E, resolveEdgeBoundary as F } from "./chart/edge-indicators.js";
|
|
5
5
|
import { computeFitToData as O } from "./chart/fit-to-data.js";
|
|
6
|
-
import { getLastValue as
|
|
7
|
-
import { resolveMaxVisibleBars as
|
|
6
|
+
import { getLastValue as Y, getPreviousClose as B, getStackedLastValue as X } from "./chart/last-value.js";
|
|
7
|
+
import { resolveMaxVisibleBars as _, resolvePadding as D, resolvePerfOptions as $, isSameHorizontalPadding as T } from "./chart/options.js";
|
|
8
8
|
import { computePan as U, computeZoom as Z } from "./chart/pan-zoom-math.js";
|
|
9
9
|
import { StreamingCadence as G } from "./chart/streaming-cadence.js";
|
|
10
10
|
import { computeStreamingTarget as j } from "./chart/streaming-target.js";
|
|
11
|
-
import { resolvePaddingTime as
|
|
12
|
-
import { computeTargetYRange as q, resolveBound as
|
|
11
|
+
import { resolvePaddingTime as C } from "./chart/viewport-padding.js";
|
|
12
|
+
import { computeTargetYRange as q, resolveBound as M } from "./chart/y-target.js";
|
|
13
13
|
import { renderCrosshair as J } from "./components/crosshair.js";
|
|
14
14
|
import { renderGrid as K } from "./components/grid.js";
|
|
15
15
|
import { renderMarker as Q } from "./components/marker.js";
|
|
@@ -17,18 +17,18 @@ import { renderReferenceLine as tt } from "./components/reference-line.js";
|
|
|
17
17
|
import { resolveTimeRegionColors as it, renderTimeRegion as et } from "./components/time-region.js";
|
|
18
18
|
import { EventEmitter as st } from "./events.js";
|
|
19
19
|
import { InteractionHandler as rt } from "./interactions/handler.js";
|
|
20
|
-
import { RenderScheduler as
|
|
20
|
+
import { RenderScheduler as R } from "./render-scheduler.js";
|
|
21
21
|
import { XScale as nt } from "./scales/x-scale.js";
|
|
22
|
-
import { YScale as
|
|
23
|
-
import { resolveSeriesDefinition as
|
|
22
|
+
import { YScale as at } from "./scales/y-scale.js";
|
|
23
|
+
import { resolveSeriesDefinition as ht } from "./series/definition.js";
|
|
24
24
|
import { catppuccin as ot } from "./theme/themes/catppuccin.js";
|
|
25
25
|
import { normalizeTime as g, detectInterval as lt } from "./utils/time.js";
|
|
26
|
-
let
|
|
27
|
-
function I(m,
|
|
28
|
-
return m ===
|
|
26
|
+
let ct = 0, dt = 0, mt = 0, ut = 0;
|
|
27
|
+
function I(m, t) {
|
|
28
|
+
return m === t ? !0 : !m || !t || m.length !== t.length ? !1 : m.every((i, e) => i === t[e]);
|
|
29
29
|
}
|
|
30
|
-
function ft(m,
|
|
31
|
-
return m ===
|
|
30
|
+
function ft(m, t) {
|
|
31
|
+
return m === t ? !0 : !m || !t || m.length !== t.length ? !1 : m.every((i, e) => i === t[e]);
|
|
32
32
|
}
|
|
33
33
|
class Ot extends st {
|
|
34
34
|
/** Canvas lifecycle and DPR-aware sizing. */
|
|
@@ -36,7 +36,7 @@ class Ot extends st {
|
|
|
36
36
|
/** Schedules main-layer redraws (background, grid, series). */
|
|
37
37
|
#i;
|
|
38
38
|
/** Schedules overlay redraws (crosshair). */
|
|
39
|
-
#
|
|
39
|
+
#l;
|
|
40
40
|
/** Maps time values to horizontal pixel coordinates. */
|
|
41
41
|
timeScale;
|
|
42
42
|
/** Maps price/value to vertical pixel coordinates. */
|
|
@@ -46,7 +46,7 @@ class Ot extends st {
|
|
|
46
46
|
/** All registered series (candlestick, line, bar, pie). */
|
|
47
47
|
#t = [];
|
|
48
48
|
/** Point annotations (event markers) drawn on the overlay layer, kept out of the series model. */
|
|
49
|
-
#
|
|
49
|
+
#m = [];
|
|
50
50
|
/** Time-region bands drawn on the main layer behind the series, kept out of the series model. */
|
|
51
51
|
#v = [];
|
|
52
52
|
/** Reference lines (horizontal threshold / vertical event) drawn on the overlay layer, kept out of the series model. */
|
|
@@ -56,32 +56,40 @@ class Ot extends st {
|
|
|
56
56
|
/** Whether to render the background grid. */
|
|
57
57
|
#E;
|
|
58
58
|
/** Detected time interval between data points (milliseconds). */
|
|
59
|
-
#
|
|
59
|
+
#a = 6e4;
|
|
60
60
|
/** Current crosshair position, null when cursor is outside the chart. */
|
|
61
|
-
#
|
|
61
|
+
#d = null;
|
|
62
|
+
/**
|
|
63
|
+
* What the crosshair is anchored to, deciding which half of the position
|
|
64
|
+
* survives a viewport move (see {@link #syncCrosshairToViewport}):
|
|
65
|
+
* `'pointer'` (real hover) keeps `mediaX`/`mediaY` and re-resolves
|
|
66
|
+
* `time`/`y`; `'time'` (programmatic {@link setCrosshair}) keeps
|
|
67
|
+
* `time`/`y` and re-projects the pixel position.
|
|
68
|
+
*/
|
|
69
|
+
#F = "pointer";
|
|
62
70
|
/** Whether the last overlay pass painted anything — gates the full-bitmap
|
|
63
71
|
* clear so an empty overlay isn't cleared again every main frame. */
|
|
64
|
-
#
|
|
72
|
+
#q = !1;
|
|
65
73
|
/** User-specified Y-axis bounds (auto, fixed, percentage). */
|
|
66
|
-
#
|
|
74
|
+
#h = {};
|
|
67
75
|
/** Cached series ID list — invalidated on add/remove. */
|
|
68
76
|
#A = null;
|
|
69
77
|
/** Axis visibility and sizing configuration. */
|
|
70
78
|
#I = {};
|
|
71
79
|
/** Host-declared state per edge — drives the edge-indicator overlay. */
|
|
72
|
-
#
|
|
80
|
+
#x = { left: "idle", right: "idle" };
|
|
73
81
|
/** Cached boundary timestamps from the last `edgeReached` emission, by side. */
|
|
74
|
-
#
|
|
82
|
+
#J = { left: null, right: null };
|
|
75
83
|
/** Host-supplied callback fired when the user releases a pan/zoom past a data edge. */
|
|
76
|
-
#
|
|
84
|
+
#K;
|
|
77
85
|
/** Nesting depth for batch updates. Suppresses recomputes while > 0. */
|
|
78
86
|
#w = 0;
|
|
79
87
|
/** True when batched operations include data changes (triggers full onDataChanged on end). */
|
|
80
|
-
|
|
88
|
+
#O = !1;
|
|
81
89
|
/** True when batched operations include visibility changes (triggers Y-range + redraw on end). */
|
|
82
|
-
#
|
|
90
|
+
#C = !1;
|
|
83
91
|
/** True when batched operations bumped overlay version (emits a single overlayChange on flush). */
|
|
84
|
-
#
|
|
92
|
+
#Y = !1;
|
|
85
93
|
/**
|
|
86
94
|
* Monotonic counter bumped on any mutation that affects overlay output —
|
|
87
95
|
* data, visibility, series options, theme. Used by snapshot helpers in
|
|
@@ -91,14 +99,14 @@ class Ot extends st {
|
|
|
91
99
|
*
|
|
92
100
|
* @internal
|
|
93
101
|
*/
|
|
94
|
-
#
|
|
102
|
+
#Q = 0;
|
|
95
103
|
get yAxisWidth() {
|
|
96
|
-
const
|
|
97
|
-
return
|
|
104
|
+
const t = this.#I.y;
|
|
105
|
+
return t?.visible === !1 ? 0 : t?.width ?? 55;
|
|
98
106
|
}
|
|
99
107
|
get xAxisHeight() {
|
|
100
|
-
const
|
|
101
|
-
return
|
|
108
|
+
const t = this.#I.x;
|
|
109
|
+
return t?.visible === !1 ? 0 : t?.height ?? 30;
|
|
102
110
|
}
|
|
103
111
|
/** Resolved animation config derived from `options.animations` at construction. */
|
|
104
112
|
#f;
|
|
@@ -126,7 +134,7 @@ class Ot extends st {
|
|
|
126
134
|
* field carries the decision across that gap. The closure also reads
|
|
127
135
|
* `#dataStart` / `#dataEnd` directly for the timestamps.
|
|
128
136
|
*/
|
|
129
|
-
#
|
|
137
|
+
#tt = !1;
|
|
130
138
|
/** Earliest data timestamp registered across all series, `null` before any data has arrived. */
|
|
131
139
|
#k = null;
|
|
132
140
|
/** Latest data timestamp registered across all series, `null` before any data has arrived. */
|
|
@@ -150,7 +158,7 @@ class Ot extends st {
|
|
|
150
158
|
* the right edge and trims the left. Resolved once at construction from
|
|
151
159
|
* `options.viewport.maxVisibleBars`.
|
|
152
160
|
*/
|
|
153
|
-
#
|
|
161
|
+
#it;
|
|
154
162
|
/**
|
|
155
163
|
* Committed logical X range — the "where X should be" target. Engine
|
|
156
164
|
* eases its visual toward this. Pan / zoom / setRange / fitContent /
|
|
@@ -166,22 +174,22 @@ class Ot extends st {
|
|
|
166
174
|
* screen; chart's per-frame autoscroll check re-engages it when a
|
|
167
175
|
* follow-up pan brings the tail back into view.
|
|
168
176
|
*/
|
|
169
|
-
#
|
|
177
|
+
#y = !0;
|
|
170
178
|
/** Resolved viewport padding (top/bottom px + left/right pixels-or-intervals). */
|
|
171
|
-
#
|
|
179
|
+
#c;
|
|
172
180
|
/** Cached chart width — passed by interactions on every call; kept for the rare
|
|
173
181
|
* call that omits it (e.g. programmatic `chart.pan(...)` without explicit width). */
|
|
174
182
|
#z = 0;
|
|
175
183
|
/** Active performance monitor, or `null` when instrumentation is disabled (the default). */
|
|
176
|
-
#
|
|
184
|
+
#S;
|
|
177
185
|
/** When true, `destroy()` tears down the monitor; false for caller-supplied monitors we must not destroy. */
|
|
178
186
|
#B = !1;
|
|
179
187
|
/** Visible HUD overlay (mounted by the perf config); non-null only when the caller requested one. */
|
|
180
|
-
#
|
|
181
|
-
constructor(
|
|
182
|
-
super(),
|
|
188
|
+
#X = null;
|
|
189
|
+
constructor(t, i) {
|
|
190
|
+
super(), i?.axis && (this.#I = i.axis, this.#h = { min: i.axis.y?.min, max: i.axis.y?.max }), this.#r = i?.theme ?? ot.theme, this.#E = i?.grid?.visible !== !1, this.#f = z.resolve(i?.animations), this.#it = _(i?.viewport?.maxVisibleBars), this.#c = D(i?.padding), this.#D = new G();
|
|
183
191
|
const e = this.#f.axis.y, s = this.#f.axis.x;
|
|
184
|
-
this.#e =
|
|
192
|
+
this.#e = H({
|
|
185
193
|
initial: { yRange: { min: 0, max: 0 }, xRange: { from: 0, to: 0 } },
|
|
186
194
|
y: {
|
|
187
195
|
curve: e.curve,
|
|
@@ -196,21 +204,21 @@ class Ot extends st {
|
|
|
196
204
|
settleMs: s.settleMs,
|
|
197
205
|
gestureMs: s.gestureMs
|
|
198
206
|
},
|
|
199
|
-
computeXTarget: () => this.#
|
|
200
|
-
computeYTarget: ({ xTarget:
|
|
207
|
+
computeXTarget: () => this.#pt(),
|
|
208
|
+
computeYTarget: ({ xTarget: h }) => this.#nt(h),
|
|
201
209
|
onWake: () => this.#i?.markDirty()
|
|
202
|
-
}), this.#
|
|
203
|
-
const r =
|
|
204
|
-
this.#
|
|
210
|
+
}), this.#K = i?.onEdgeReached, this.#N = i?.viewport?.initialRange;
|
|
211
|
+
const r = $(i?.perf);
|
|
212
|
+
this.#S = r.monitor, this.#B = r.ownsMonitor, this.#n = new W(t, this.#S ?? void 0), this.timeScale = new nt(), this.yScale = new at();
|
|
205
213
|
const n = this.#f.axis.ticksMs;
|
|
206
|
-
this.timeScale.tickTracker.setFadeMs(n), this.yScale.tickTracker.setFadeMs(n), this.#i = new
|
|
207
|
-
(
|
|
208
|
-
), this.#
|
|
209
|
-
const
|
|
210
|
-
this.#W =
|
|
211
|
-
this.syncScales(), this.#
|
|
212
|
-
}), this.syncScales(), this.#W?.on("crosshairMove", (
|
|
213
|
-
this.#
|
|
214
|
+
this.timeScale.tickTracker.setFadeMs(n), this.yScale.tickTracker.setFadeMs(n), this.#i = new R((h) => this.#G(h)), this.#l = new R(
|
|
215
|
+
(h) => this.#j("overlay", h, () => this.renderOverlay(h))
|
|
216
|
+
), this.#S && r.hud && (this.#X = r.hud(t, this.#S));
|
|
217
|
+
const a = i?.interactive !== !1;
|
|
218
|
+
this.#W = a ? new rt(this.#n.canvas, this, this.timeScale, this.yScale) : null, this.#n.on("resize", () => {
|
|
219
|
+
this.syncScales(), this.#G(), this.emit("viewportChange");
|
|
220
|
+
}), this.syncScales(), this.#W?.on("crosshairMove", (h) => {
|
|
221
|
+
this.#F = "pointer", this.#d = h, this.#l.markDirty(), this.emit("crosshairMove", h), this.#_(h);
|
|
214
222
|
});
|
|
215
223
|
}
|
|
216
224
|
/**
|
|
@@ -225,9 +233,9 @@ class Ot extends st {
|
|
|
225
233
|
* `setSeriesVisible` / `setLayerVisible` calls — produce one overlay
|
|
226
234
|
* render instead of N.
|
|
227
235
|
*/
|
|
228
|
-
#
|
|
229
|
-
if (this.#
|
|
230
|
-
this.#
|
|
236
|
+
#b() {
|
|
237
|
+
if (this.#Q++, this.#w > 0) {
|
|
238
|
+
this.#Y = !0;
|
|
231
239
|
return;
|
|
232
240
|
}
|
|
233
241
|
this.emit("overlayChange");
|
|
@@ -241,39 +249,39 @@ class Ot extends st {
|
|
|
241
249
|
* @internal
|
|
242
250
|
*/
|
|
243
251
|
getOverlayVersion() {
|
|
244
|
-
return this.#
|
|
252
|
+
return this.#Q;
|
|
245
253
|
}
|
|
246
254
|
/**
|
|
247
255
|
* Return a series ID: use the provided hint if it's non-empty and not already taken,
|
|
248
256
|
* otherwise generate a new auto ID. Auto-generated IDs never collide with each other
|
|
249
257
|
* or with user-provided IDs because they use a monotonically increasing counter.
|
|
250
258
|
*/
|
|
251
|
-
#
|
|
252
|
-
if (
|
|
253
|
-
return
|
|
254
|
-
let
|
|
259
|
+
#lt(t) {
|
|
260
|
+
if (t && !this.#t.some((e) => e.id === t))
|
|
261
|
+
return t;
|
|
262
|
+
let i;
|
|
255
263
|
do
|
|
256
|
-
|
|
257
|
-
while (this.#t.some((e) => e.id ===
|
|
258
|
-
return
|
|
264
|
+
i = `series_${++ct}`;
|
|
265
|
+
while (this.#t.some((e) => e.id === i));
|
|
266
|
+
return i;
|
|
259
267
|
}
|
|
260
|
-
addSeries(
|
|
261
|
-
const e = typeof
|
|
268
|
+
addSeries(t, i = {}) {
|
|
269
|
+
const e = typeof t == "string" ? ht(t) : t;
|
|
262
270
|
if (!e)
|
|
263
271
|
throw new Error(
|
|
264
|
-
`[wick-charts] Unknown series type '${String(
|
|
272
|
+
`[wick-charts] Unknown series type '${String(t)}'. The string form of addSeries resolves through the series registry — call registerBuiltinSeries() once at startup, or pass the definition directly: chart.addSeries(LineSeriesDef, options).`
|
|
265
273
|
);
|
|
266
|
-
const { layers: s, id: r, label: n, labels:
|
|
274
|
+
const { layers: s, id: r, label: n, labels: a, ...h } = i, l = s ?? 1, o = {
|
|
267
275
|
...this.#f.defaults(e.type),
|
|
268
|
-
...
|
|
276
|
+
...h,
|
|
269
277
|
...this.#f.overrides(e.type)
|
|
270
|
-
}, u = e.create({ theme: this.#r, layerCount:
|
|
271
|
-
return u.onDataChanged?.(() => this.onDataChanged()), this.#t.push({ id:
|
|
278
|
+
}, u = e.create({ theme: this.#r, layerCount: l }, o), d = a ?? (typeof n == "string" ? [n] : void 0), c = this.#lt(r);
|
|
279
|
+
return u.onDataChanged?.(() => this.onDataChanged()), this.#t.push({ id: c, labels: d, renderer: u, visible: !0 }), this.#A = null, this.emit("seriesChange"), this.#b(), c;
|
|
272
280
|
}
|
|
273
281
|
/** Remove a series by ID and clean up its resources. */
|
|
274
|
-
removeSeries(
|
|
275
|
-
const
|
|
276
|
-
|
|
282
|
+
removeSeries(t) {
|
|
283
|
+
const i = this.#t.findIndex((e) => e.id === t);
|
|
284
|
+
i >= 0 && (this.#t[i].renderer.dispose(), this.#t.splice(i, 1), this.#A = null, this.#i.markDirty(), this.emit("seriesChange"), this.#b());
|
|
277
285
|
}
|
|
278
286
|
// --- Markers (point annotations) -----------------------------------------
|
|
279
287
|
/**
|
|
@@ -285,71 +293,71 @@ class Ot extends st {
|
|
|
285
293
|
* are excluded from tooltips, the legend, and the Y-range autoscale, and never
|
|
286
294
|
* appear in series queries.
|
|
287
295
|
*/
|
|
288
|
-
addMarker(
|
|
289
|
-
const
|
|
290
|
-
return this.#
|
|
296
|
+
addMarker(t) {
|
|
297
|
+
const i = `marker_${++dt}`;
|
|
298
|
+
return this.#m.push(this.#et(i, t)), this.#l.markDirty(), i;
|
|
291
299
|
}
|
|
292
300
|
/** Replace a marker's config in place. No-op when the id is unknown. */
|
|
293
|
-
updateMarker(
|
|
294
|
-
const e = this.#
|
|
295
|
-
e !== -1 && (this.#
|
|
301
|
+
updateMarker(t, i) {
|
|
302
|
+
const e = this.#m.findIndex((s) => s.id === t);
|
|
303
|
+
e !== -1 && (this.#m[e] = this.#et(t, i), this.#l.markDirty());
|
|
296
304
|
}
|
|
297
305
|
/** Remove a marker by id. No-op when the id is unknown. */
|
|
298
|
-
removeMarker(
|
|
299
|
-
const
|
|
300
|
-
|
|
306
|
+
removeMarker(t) {
|
|
307
|
+
const i = this.#m.findIndex((e) => e.id === t);
|
|
308
|
+
i !== -1 && (this.#m.splice(i, 1), this.#l.markDirty());
|
|
301
309
|
}
|
|
302
310
|
/** Read-only snapshot of the registered markers (normalized). Mutate via add / update / removeMarker. */
|
|
303
311
|
getMarkers() {
|
|
304
|
-
return this.#
|
|
312
|
+
return this.#m;
|
|
305
313
|
}
|
|
306
|
-
#
|
|
314
|
+
#et(t, i) {
|
|
307
315
|
return {
|
|
308
|
-
id:
|
|
309
|
-
time: g(
|
|
310
|
-
value:
|
|
311
|
-
seriesId:
|
|
312
|
-
shape:
|
|
313
|
-
pulse:
|
|
314
|
-
label:
|
|
315
|
-
color:
|
|
316
|
+
id: t,
|
|
317
|
+
time: g(i.time),
|
|
318
|
+
value: i.value,
|
|
319
|
+
seriesId: i.seriesId,
|
|
320
|
+
shape: i.shape ?? "dot",
|
|
321
|
+
pulse: i.pulse ?? !1,
|
|
322
|
+
label: i.label,
|
|
323
|
+
color: i.color
|
|
316
324
|
};
|
|
317
325
|
}
|
|
318
326
|
/** Resolve a marker's Y value: explicit `value`, else snapped to its series at `time`. Null when neither resolves. */
|
|
319
|
-
#
|
|
320
|
-
if (
|
|
321
|
-
if (
|
|
322
|
-
const
|
|
323
|
-
return
|
|
327
|
+
#ct(t) {
|
|
328
|
+
if (t.value !== void 0 && Number.isFinite(t.value)) return t.value;
|
|
329
|
+
if (t.seriesId === void 0) return null;
|
|
330
|
+
const i = this.getDataAtTime(t.seriesId, t.time);
|
|
331
|
+
return i ? "close" in i ? i.close : i.value : null;
|
|
324
332
|
}
|
|
325
333
|
/** Resolve a marker's color: explicit `color`, else the series color, else the theme line color. */
|
|
326
|
-
#
|
|
327
|
-
if (
|
|
328
|
-
if (
|
|
329
|
-
const
|
|
330
|
-
if (
|
|
334
|
+
#dt(t) {
|
|
335
|
+
if (t.color !== void 0) return t.color;
|
|
336
|
+
if (t.seriesId !== void 0) {
|
|
337
|
+
const i = this.getSeriesColor(t.seriesId);
|
|
338
|
+
if (i !== null) return i;
|
|
331
339
|
}
|
|
332
340
|
return this.#r.line.color;
|
|
333
341
|
}
|
|
334
|
-
#
|
|
335
|
-
if (this.#
|
|
336
|
-
const
|
|
337
|
-
for (const e of this.#
|
|
338
|
-
const s = this.#
|
|
342
|
+
#mt(t) {
|
|
343
|
+
if (this.#m.length === 0) return;
|
|
344
|
+
const i = performance.now() / (N * 2 * Math.PI) % 1;
|
|
345
|
+
for (const e of this.#m) {
|
|
346
|
+
const s = this.#ct(e);
|
|
339
347
|
s !== null && Q({
|
|
340
|
-
scope:
|
|
348
|
+
scope: t,
|
|
341
349
|
timeScale: this.timeScale,
|
|
342
350
|
yScale: this.yScale,
|
|
343
351
|
theme: this.#r,
|
|
344
352
|
marker: {
|
|
345
353
|
time: e.time,
|
|
346
354
|
value: s,
|
|
347
|
-
color: this.#
|
|
355
|
+
color: this.#dt(e),
|
|
348
356
|
shape: e.shape,
|
|
349
357
|
pulse: e.pulse,
|
|
350
358
|
label: e.label
|
|
351
359
|
},
|
|
352
|
-
phase:
|
|
360
|
+
phase: i
|
|
353
361
|
});
|
|
354
362
|
}
|
|
355
363
|
}
|
|
@@ -365,45 +373,45 @@ class Ot extends st {
|
|
|
365
373
|
* series model — they never affect the Y-range autoscale, tooltips, or the
|
|
366
374
|
* legend.
|
|
367
375
|
*/
|
|
368
|
-
addRegion(
|
|
369
|
-
const
|
|
370
|
-
return this.#v.push(this.#
|
|
376
|
+
addRegion(t) {
|
|
377
|
+
const i = `region_${++mt}`;
|
|
378
|
+
return this.#v.push(this.#st(i, t)), this.#i.markDirty(), i;
|
|
371
379
|
}
|
|
372
380
|
/** Replace a region's config in place. No-op when the id is unknown. */
|
|
373
|
-
updateRegion(
|
|
374
|
-
const e = this.#v.findIndex((s) => s.id ===
|
|
375
|
-
e !== -1 && (this.#v[e] = this.#
|
|
381
|
+
updateRegion(t, i) {
|
|
382
|
+
const e = this.#v.findIndex((s) => s.id === t);
|
|
383
|
+
e !== -1 && (this.#v[e] = this.#st(t, i), this.#i.markDirty());
|
|
376
384
|
}
|
|
377
385
|
/** Remove a region by id. No-op when the id is unknown. */
|
|
378
|
-
removeRegion(
|
|
379
|
-
const
|
|
380
|
-
|
|
386
|
+
removeRegion(t) {
|
|
387
|
+
const i = this.#v.findIndex((e) => e.id === t);
|
|
388
|
+
i !== -1 && (this.#v.splice(i, 1), this.#i.markDirty());
|
|
381
389
|
}
|
|
382
390
|
/** Read-only snapshot of the registered regions (normalized). Mutate via add / update / removeRegion. */
|
|
383
391
|
getRegions() {
|
|
384
392
|
return this.#v;
|
|
385
393
|
}
|
|
386
|
-
#
|
|
387
|
-
const { to: e } =
|
|
394
|
+
#st(t, i) {
|
|
395
|
+
const { to: e } = i, s = e === void 0 || e === "now" ? null : g(e);
|
|
388
396
|
return {
|
|
389
|
-
id:
|
|
390
|
-
from: g(
|
|
397
|
+
id: t,
|
|
398
|
+
from: g(i.from),
|
|
391
399
|
to: s,
|
|
392
|
-
fill:
|
|
393
|
-
color:
|
|
394
|
-
label:
|
|
400
|
+
fill: i.fill,
|
|
401
|
+
color: i.color,
|
|
402
|
+
label: i.label
|
|
395
403
|
};
|
|
396
404
|
}
|
|
397
|
-
#
|
|
405
|
+
#ut(t, i, e) {
|
|
398
406
|
if (this.#v.length !== 0)
|
|
399
407
|
for (const s of this.#v) {
|
|
400
408
|
const { color: r, fill: n } = it(s, this.#r);
|
|
401
409
|
et({
|
|
402
|
-
scope:
|
|
410
|
+
scope: t,
|
|
403
411
|
timeScale: this.timeScale,
|
|
404
412
|
theme: this.#r,
|
|
405
413
|
region: { from: s.from, to: s.to, fill: n, color: r, label: s.label },
|
|
406
|
-
plotWidth:
|
|
414
|
+
plotWidth: i,
|
|
407
415
|
plotHeight: e
|
|
408
416
|
});
|
|
409
417
|
}
|
|
@@ -419,39 +427,39 @@ class Ot extends st {
|
|
|
419
427
|
* series model — they never affect the Y-range autoscale, tooltips, or the
|
|
420
428
|
* legend.
|
|
421
429
|
*/
|
|
422
|
-
addLine(
|
|
423
|
-
const
|
|
424
|
-
return this.#g.push(this.#
|
|
430
|
+
addLine(t) {
|
|
431
|
+
const i = `line_${++ut}`;
|
|
432
|
+
return this.#g.push(this.#rt(i, t)), this.#l.markDirty(), i;
|
|
425
433
|
}
|
|
426
434
|
/** Replace a reference line's config in place. No-op when the id is unknown. */
|
|
427
|
-
updateLine(
|
|
428
|
-
const e = this.#g.findIndex((s) => s.id ===
|
|
429
|
-
e !== -1 && (this.#g[e] = this.#
|
|
435
|
+
updateLine(t, i) {
|
|
436
|
+
const e = this.#g.findIndex((s) => s.id === t);
|
|
437
|
+
e !== -1 && (this.#g[e] = this.#rt(t, i), this.#l.markDirty());
|
|
430
438
|
}
|
|
431
439
|
/** Remove a reference line by id. No-op when the id is unknown. */
|
|
432
|
-
removeLine(
|
|
433
|
-
const
|
|
434
|
-
|
|
440
|
+
removeLine(t) {
|
|
441
|
+
const i = this.#g.findIndex((e) => e.id === t);
|
|
442
|
+
i !== -1 && (this.#g.splice(i, 1), this.#l.markDirty());
|
|
435
443
|
}
|
|
436
444
|
/** Read-only snapshot of the registered reference lines (normalized). Mutate via add / update / removeLine. */
|
|
437
445
|
getLines() {
|
|
438
446
|
return this.#g;
|
|
439
447
|
}
|
|
440
|
-
#
|
|
441
|
-
const { time: e, value: s } =
|
|
442
|
-
id:
|
|
443
|
-
color:
|
|
444
|
-
label:
|
|
445
|
-
style:
|
|
446
|
-
width:
|
|
448
|
+
#rt(t, i) {
|
|
449
|
+
const { time: e, value: s } = i, r = {
|
|
450
|
+
id: t,
|
|
451
|
+
color: i.color,
|
|
452
|
+
label: i.label,
|
|
453
|
+
style: i.style ?? "dashed",
|
|
454
|
+
width: i.width ?? 1
|
|
447
455
|
};
|
|
448
456
|
return e !== void 0 ? { ...r, orientation: "vertical", coord: g(e) } : { ...r, orientation: "horizontal", coord: s ?? Number.NaN };
|
|
449
457
|
}
|
|
450
|
-
#
|
|
458
|
+
#ft(t, i, e) {
|
|
451
459
|
if (this.#g.length !== 0)
|
|
452
460
|
for (const s of this.#g)
|
|
453
461
|
tt({
|
|
454
|
-
scope:
|
|
462
|
+
scope: t,
|
|
455
463
|
timeScale: this.timeScale,
|
|
456
464
|
yScale: this.yScale,
|
|
457
465
|
theme: this.#r,
|
|
@@ -463,7 +471,7 @@ class Ot extends st {
|
|
|
463
471
|
width: s.width,
|
|
464
472
|
label: s.label
|
|
465
473
|
},
|
|
466
|
-
plotWidth:
|
|
474
|
+
plotWidth: i,
|
|
467
475
|
plotHeight: e
|
|
468
476
|
});
|
|
469
477
|
}
|
|
@@ -485,18 +493,18 @@ class Ot extends st {
|
|
|
485
493
|
* For line/bar accepts `TimePointInput[]` (time may be `Date`); for candlestick accepts `OHLCInput[]`;
|
|
486
494
|
* for pie accepts `PieSliceData[]`. Time fields are normalized internally.
|
|
487
495
|
*/
|
|
488
|
-
setSeriesData(
|
|
489
|
-
const s = this.#t.find((r) => r.id ===
|
|
490
|
-
s && (this
|
|
496
|
+
setSeriesData(t, i, e) {
|
|
497
|
+
const s = this.#t.find((r) => r.id === t);
|
|
498
|
+
s && (this.#$ = !0, s.renderer.setData(i, e));
|
|
491
499
|
}
|
|
492
500
|
/** Append a new data point to the end of a series (real-time tick). */
|
|
493
|
-
appendData(
|
|
494
|
-
const s = this.#t.find((r) => r.id ===
|
|
495
|
-
s === void 0 || s.renderer.appendPoint === void 0 || s.renderer.appendPoint(
|
|
501
|
+
appendData(t, i, e) {
|
|
502
|
+
const s = this.#t.find((r) => r.id === t);
|
|
503
|
+
s === void 0 || s.renderer.appendPoint === void 0 || s.renderer.appendPoint(i, e);
|
|
496
504
|
}
|
|
497
505
|
/** Update the last data point of a series in place (e.g. live candle update). */
|
|
498
|
-
updateData(
|
|
499
|
-
this.#t.find((s) => s.id ===
|
|
506
|
+
updateData(t, i, e) {
|
|
507
|
+
this.#t.find((s) => s.id === t)?.renderer.updateLastPoint?.(i, e);
|
|
500
508
|
}
|
|
501
509
|
/**
|
|
502
510
|
* Keep only the most recent `count` points of a series — drop the oldest
|
|
@@ -511,36 +519,36 @@ class Ot extends st {
|
|
|
511
519
|
* chart.keepLast('feed', 100);
|
|
512
520
|
* ```
|
|
513
521
|
*/
|
|
514
|
-
keepLast(
|
|
515
|
-
this.#t.find((s) => s.id ===
|
|
522
|
+
keepLast(t, i, e) {
|
|
523
|
+
this.#t.find((s) => s.id === t)?.renderer.keepLast?.(i, e);
|
|
516
524
|
}
|
|
517
525
|
/** Update visual options (color, width, etc.) for an existing series. */
|
|
518
|
-
updateSeriesOptions(
|
|
519
|
-
const e = this.#t.find((
|
|
526
|
+
updateSeriesOptions(t, i) {
|
|
527
|
+
const e = this.#t.find((o) => o.id === t);
|
|
520
528
|
if (!e) return;
|
|
521
529
|
const s = e.labels, r = e.renderer.getLayerColors().slice(), n = this.#f.overrides(e.renderer.kind);
|
|
522
|
-
e.renderer.updateOptions({ ...
|
|
523
|
-
const
|
|
524
|
-
(
|
|
530
|
+
e.renderer.updateOptions({ ...i, ...n }), "label" in i && typeof i.label == "string" && (e.labels = [i.label]), this.#i.markDirty();
|
|
531
|
+
const a = e.renderer.getLayerColors(), h = r.length !== a.length || r.some((o, u) => o !== a[u]), l = !I(s, e.labels);
|
|
532
|
+
(h || l) && this.#b();
|
|
525
533
|
}
|
|
526
534
|
/**
|
|
527
535
|
* Batch multiple updates: suppress recomputes until `fn` returns. Exceptions
|
|
528
536
|
* inside `fn` still flush the batch so counters don't leak across calls.
|
|
529
537
|
*/
|
|
530
|
-
batch(
|
|
538
|
+
batch(t) {
|
|
531
539
|
this.#w++;
|
|
532
540
|
try {
|
|
533
|
-
|
|
541
|
+
t();
|
|
534
542
|
} finally {
|
|
535
543
|
if (--this.#w <= 0) {
|
|
536
|
-
if (this.#w = 0, this
|
|
537
|
-
this
|
|
538
|
-
else if (this.#
|
|
539
|
-
this.#
|
|
540
|
-
const
|
|
541
|
-
this.#e.onAxisReconfig(
|
|
544
|
+
if (this.#w = 0, this.#O)
|
|
545
|
+
this.#O = !1, this.#C = !1, this.onDataChanged();
|
|
546
|
+
else if (this.#C) {
|
|
547
|
+
this.#C = !1, this.#u = !0;
|
|
548
|
+
const i = performance.now();
|
|
549
|
+
this.#e.onAxisReconfig(i), this.#p(i), this.#i.markDirty();
|
|
542
550
|
}
|
|
543
|
-
this.#
|
|
551
|
+
this.#Y && (this.#Y = !1, this.emit("overlayChange"));
|
|
544
552
|
}
|
|
545
553
|
}
|
|
546
554
|
}
|
|
@@ -551,18 +559,18 @@ class Ot extends st {
|
|
|
551
559
|
* computation immediately so the axis can start moving while the line
|
|
552
560
|
* fades out in parallel.
|
|
553
561
|
*/
|
|
554
|
-
setSeriesVisible(
|
|
555
|
-
const e = this.#t.find((n) => n.id ===
|
|
556
|
-
if (!e || e.visible ===
|
|
557
|
-
e.visible =
|
|
562
|
+
setSeriesVisible(t, i) {
|
|
563
|
+
const e = this.#t.find((n) => n.id === t);
|
|
564
|
+
if (!e || e.visible === i) return;
|
|
565
|
+
e.visible = i, this.#b();
|
|
558
566
|
const s = this.#f.toggleMs;
|
|
559
|
-
if (e.renderer.setAlpha?.(
|
|
560
|
-
this.#
|
|
567
|
+
if (e.renderer.setAlpha?.(i ? 1 : 0, s), this.#i.markDirty(), this.#w > 0) {
|
|
568
|
+
this.#C = !0;
|
|
561
569
|
return;
|
|
562
570
|
}
|
|
563
571
|
this.#u = !0;
|
|
564
572
|
const r = performance.now();
|
|
565
|
-
this.#e.onSeriesVisibilityChanged(r), this.#
|
|
573
|
+
this.#e.onSeriesVisibilityChanged(r), this.#p(r);
|
|
566
574
|
}
|
|
567
575
|
/**
|
|
568
576
|
* Whether streaming ticks slide the visible range to track the data tail.
|
|
@@ -572,10 +580,10 @@ class Ot extends st {
|
|
|
572
580
|
* containing the tail.
|
|
573
581
|
*/
|
|
574
582
|
getAutoScroll() {
|
|
575
|
-
return this.#
|
|
583
|
+
return this.#y;
|
|
576
584
|
}
|
|
577
|
-
isSeriesVisible(
|
|
578
|
-
return this.#t.find((
|
|
585
|
+
isSeriesVisible(t) {
|
|
586
|
+
return this.#t.find((i) => i.id === t)?.visible ?? !0;
|
|
579
587
|
}
|
|
580
588
|
/**
|
|
581
589
|
* Show or hide a specific layer within a multi-layer series.
|
|
@@ -587,28 +595,28 @@ class Ot extends st {
|
|
|
587
595
|
* `getValueRange` reading `store.isVisible()`); the alpha fade keeps the
|
|
588
596
|
* layer's geometry on screen until the engine's Y ease settles.
|
|
589
597
|
*/
|
|
590
|
-
setLayerVisible(
|
|
591
|
-
const s = this.#t.find((
|
|
592
|
-
if (!s || s.renderer.getLayerCount() <= 1 || s.renderer.isLayerVisible(
|
|
593
|
-
s.renderer.setLayerVisible(
|
|
598
|
+
setLayerVisible(t, i, e) {
|
|
599
|
+
const s = this.#t.find((a) => a.id === t);
|
|
600
|
+
if (!s || s.renderer.getLayerCount() <= 1 || s.renderer.isLayerVisible(i) === e) return;
|
|
601
|
+
s.renderer.setLayerVisible(i, e), this.#b();
|
|
594
602
|
const r = this.#f.toggleMs;
|
|
595
|
-
if (s.renderer.setLayerAlpha?.(
|
|
596
|
-
this.#
|
|
603
|
+
if (s.renderer.setLayerAlpha?.(i, e ? 1 : 0, r), this.#i.markDirty(), this.#w > 0) {
|
|
604
|
+
this.#C = !0;
|
|
597
605
|
return;
|
|
598
606
|
}
|
|
599
607
|
this.#u = !0;
|
|
600
608
|
const n = performance.now();
|
|
601
|
-
this.#e.onSeriesVisibilityChanged(n), this.#
|
|
609
|
+
this.#e.onSeriesVisibilityChanged(n), this.#p(n);
|
|
602
610
|
}
|
|
603
|
-
isLayerVisible(
|
|
604
|
-
return this.#t.find((e) => e.id ===
|
|
611
|
+
isLayerVisible(t, i) {
|
|
612
|
+
return this.#t.find((e) => e.id === t)?.renderer.isLayerVisible(i) ?? !0;
|
|
605
613
|
}
|
|
606
614
|
/** Auto-fit the viewport to show all data across every series. */
|
|
607
615
|
fitContent() {
|
|
608
|
-
const { first:
|
|
609
|
-
if (
|
|
616
|
+
const { first: t, last: i } = this.getDataBounds();
|
|
617
|
+
if (t === void 0 || i === void 0) return;
|
|
610
618
|
const e = this.#n.size.media.width - this.yAxisWidth;
|
|
611
|
-
this.#
|
|
619
|
+
this.#R(t, i, e), this.#H({ xEase: !0 });
|
|
612
620
|
}
|
|
613
621
|
getVisibleRange() {
|
|
614
622
|
return this.#o.from === 0 && this.#o.to === 0 ? this.#e.getAnimationState().xRange : this.#o;
|
|
@@ -645,35 +653,35 @@ class Ot extends st {
|
|
|
645
653
|
* multi-chart sync so a synced pane tracks the originating gesture in
|
|
646
654
|
* lockstep rather than lagging behind on the long sticky contract.
|
|
647
655
|
*/
|
|
648
|
-
setVisibleRange(
|
|
649
|
-
if (typeof
|
|
650
|
-
if (!Number.isInteger(
|
|
651
|
-
const { first: n, last:
|
|
652
|
-
if (n === void 0 ||
|
|
653
|
-
const
|
|
654
|
-
this.#
|
|
656
|
+
setVisibleRange(t, i) {
|
|
657
|
+
if (typeof t == "number") {
|
|
658
|
+
if (!Number.isInteger(t) || t < 2) return;
|
|
659
|
+
const { first: n, last: a } = this.getDataBounds();
|
|
660
|
+
if (n === void 0 || a === void 0) return;
|
|
661
|
+
const h = Math.max(n, a - (t - 1) * this.#a), l = this.#n.size.media.width - this.yAxisWidth;
|
|
662
|
+
this.#R(h, a, l), this.#H();
|
|
655
663
|
return;
|
|
656
664
|
}
|
|
657
|
-
if ("bars" in
|
|
658
|
-
if (!Number.isInteger(
|
|
659
|
-
const n = g(
|
|
660
|
-
this.#
|
|
665
|
+
if ("bars" in t) {
|
|
666
|
+
if (!Number.isInteger(t.bars) || t.bars < 2) return;
|
|
667
|
+
const n = g(t.from), a = n + t.bars * this.#a;
|
|
668
|
+
this.#y = this.#s !== null && this.#s >= n && this.#s <= a, this.#T({ from: n, to: a }, { emitChange: !0 }), this.#L = !0, this.#H();
|
|
661
669
|
return;
|
|
662
670
|
}
|
|
663
|
-
const e = g(
|
|
664
|
-
this.#
|
|
665
|
-
const r = this.#
|
|
666
|
-
if (
|
|
667
|
-
r && this.#
|
|
671
|
+
const e = g(t.from), s = g(t.to);
|
|
672
|
+
this.#y = this.#s !== null && this.#s >= e && this.#s <= s, this.#L = !1;
|
|
673
|
+
const r = this.#T({ from: e, to: s }, { emitChange: !0 });
|
|
674
|
+
if (i?.gesture) {
|
|
675
|
+
r && this.#vt();
|
|
668
676
|
return;
|
|
669
677
|
}
|
|
670
|
-
this.#
|
|
678
|
+
this.#H();
|
|
671
679
|
}
|
|
672
680
|
getYRange() {
|
|
673
681
|
return this.#V;
|
|
674
682
|
}
|
|
675
683
|
getCrosshairPosition() {
|
|
676
|
-
return this.#
|
|
684
|
+
return this.#d;
|
|
677
685
|
}
|
|
678
686
|
/**
|
|
679
687
|
* Programmatically set or clear the crosshair. Same effect as the user
|
|
@@ -700,45 +708,52 @@ class Ot extends st {
|
|
|
700
708
|
* current crosshair is a no-op (no emit). Combined with the y-preservation
|
|
701
709
|
* above, this lets N charts broadcast their hover to each other without
|
|
702
710
|
* a feedback-loop guard.
|
|
711
|
+
*
|
|
712
|
+
* The echo protection assumes the broadcast is **synchronous** (call
|
|
713
|
+
* `setCrosshair` directly from the `crosshairMove` listener). An echo
|
|
714
|
+
* routed through an async hop — a state store, a React render — can
|
|
715
|
+
* arrive with a stale `time` while the origin chart streams, miss the
|
|
716
|
+
* idempotency check, and flip that chart to time-anchored until the next
|
|
717
|
+
* real pointer move (see {@link #syncCrosshairToViewport}).
|
|
703
718
|
*/
|
|
704
|
-
setCrosshair(
|
|
705
|
-
if (
|
|
706
|
-
if (this.#
|
|
707
|
-
this.#
|
|
719
|
+
setCrosshair(t) {
|
|
720
|
+
if (t === null) {
|
|
721
|
+
if (this.#d === null) return;
|
|
722
|
+
this.#d = null, this.#l.markDirty(), this.emit("crosshairMove", null);
|
|
708
723
|
return;
|
|
709
724
|
}
|
|
710
|
-
const { time:
|
|
711
|
-
if (!Number.isFinite(
|
|
712
|
-
const e = this.#
|
|
725
|
+
const { time: i } = t;
|
|
726
|
+
if (!Number.isFinite(i)) return;
|
|
727
|
+
const e = this.#d;
|
|
713
728
|
let s;
|
|
714
|
-
if (
|
|
715
|
-
s =
|
|
729
|
+
if (t.y !== void 0)
|
|
730
|
+
s = t.y;
|
|
716
731
|
else if (e !== null)
|
|
717
732
|
s = e.y;
|
|
718
733
|
else {
|
|
719
|
-
const
|
|
720
|
-
s = (
|
|
734
|
+
const a = this.#V;
|
|
735
|
+
s = (a.min + a.max) / 2;
|
|
721
736
|
}
|
|
722
|
-
if (!Number.isFinite(s) || e !== null && e.time ===
|
|
723
|
-
const r = this.timeScale.timeToX(
|
|
724
|
-
this.#
|
|
737
|
+
if (!Number.isFinite(s) || e !== null && e.time === i && e.y === s) return;
|
|
738
|
+
const r = this.timeScale.timeToX(i), n = this.yScale.valueToY(s);
|
|
739
|
+
this.#F = "time", this.#d = { mediaX: r, mediaY: n, time: i, y: s }, this.#l.markDirty(), this.emit("crosshairMove", this.#d);
|
|
725
740
|
}
|
|
726
741
|
/** Get the last visible value and whether the absolute last point is on screen. */
|
|
727
|
-
getLastValue(
|
|
728
|
-
return
|
|
742
|
+
getLastValue(t) {
|
|
743
|
+
return Y(t, this.#t, this.#e.getAnimationState().xRange);
|
|
729
744
|
}
|
|
730
745
|
/** Get the second-to-last value, useful for computing change. */
|
|
731
|
-
getPreviousClose(
|
|
732
|
-
return
|
|
746
|
+
getPreviousClose(t) {
|
|
747
|
+
return B(t, this.#t);
|
|
733
748
|
}
|
|
734
|
-
getLastData(
|
|
735
|
-
const
|
|
736
|
-
return !
|
|
749
|
+
getLastData(t) {
|
|
750
|
+
const i = this.#t.find((e) => e.id === t);
|
|
751
|
+
return !i || i.renderer.kind === "pie" ? null : i.renderer.getLastDataPoint();
|
|
737
752
|
}
|
|
738
753
|
/** Find the data point closest to the given timestamp within one data interval. */
|
|
739
|
-
getDataAtTime(
|
|
740
|
-
const e = this.#t.find((s) => s.id ===
|
|
741
|
-
return e ? e.renderer.getDataAtTime?.(
|
|
754
|
+
getDataAtTime(t, i) {
|
|
755
|
+
const e = this.#t.find((s) => s.id === t);
|
|
756
|
+
return e ? e.renderer.getDataAtTime?.(i, this.#a) ?? null : null;
|
|
742
757
|
}
|
|
743
758
|
/**
|
|
744
759
|
* Get all layers' data at a given time for multi-layer series (Bar/Line
|
|
@@ -746,21 +761,21 @@ class Ot extends st {
|
|
|
746
761
|
* snapped sample time — callers must not derive layer identity from the
|
|
747
762
|
* array index, because hidden layers are filtered out.
|
|
748
763
|
*/
|
|
749
|
-
getLayerSnapshots(
|
|
750
|
-
const e = this.#t.find((s) => s.id ===
|
|
751
|
-
return e?.visible ? e.renderer.getLayerSnapshots?.(
|
|
764
|
+
getLayerSnapshots(t, i) {
|
|
765
|
+
const e = this.#t.find((s) => s.id === t);
|
|
766
|
+
return e?.visible ? e.renderer.getLayerSnapshots?.(i, this.#a) ?? null : null;
|
|
752
767
|
}
|
|
753
768
|
getSeriesIds() {
|
|
754
|
-
return this.#A || (this.#A = this.#t.map((
|
|
769
|
+
return this.#A || (this.#A = this.#t.map((t) => t.id)), this.#A.slice();
|
|
755
770
|
}
|
|
756
771
|
/**
|
|
757
772
|
* True if any visible series is non-pie (line / bar / candlestick). Used
|
|
758
773
|
* to gate crosshair rendering — a pie-only chart has no meaningful x/y
|
|
759
774
|
* coordinate system, so the dashed hairlines would just be visual noise.
|
|
760
775
|
*/
|
|
761
|
-
#
|
|
762
|
-
for (const
|
|
763
|
-
if (
|
|
776
|
+
#gt() {
|
|
777
|
+
for (const t of this.#t)
|
|
778
|
+
if (t.visible && t.renderer.kind !== "pie")
|
|
764
779
|
return !0;
|
|
765
780
|
return !1;
|
|
766
781
|
}
|
|
@@ -773,23 +788,23 @@ class Ot extends st {
|
|
|
773
788
|
* - `opts.singleLayerOnly` — exclude series with more than one layer.
|
|
774
789
|
* Useful for YLabel fallback priority (stick to a single line first).
|
|
775
790
|
*/
|
|
776
|
-
getSeriesIdsByType(
|
|
777
|
-
const e =
|
|
791
|
+
getSeriesIdsByType(t, i) {
|
|
792
|
+
const e = i?.visibleOnly === !0, s = i?.singleLayerOnly === !0, r = [];
|
|
778
793
|
for (const n of this.#t) {
|
|
779
|
-
const
|
|
780
|
-
if (
|
|
781
|
-
const
|
|
782
|
-
if (!(s &&
|
|
794
|
+
const a = n.renderer.kind === "pie";
|
|
795
|
+
if (t === "pie" && !a || t === "time" && a) continue;
|
|
796
|
+
const h = n.renderer.getLayerCount();
|
|
797
|
+
if (!(s && h > 1)) {
|
|
783
798
|
if (e) {
|
|
784
799
|
if (!n.visible) continue;
|
|
785
|
-
if (
|
|
786
|
-
let
|
|
787
|
-
for (let
|
|
788
|
-
if (n.renderer.isLayerVisible(
|
|
789
|
-
|
|
800
|
+
if (h > 1) {
|
|
801
|
+
let l = !1;
|
|
802
|
+
for (let o = 0; o < h; o++)
|
|
803
|
+
if (n.renderer.isLayerVisible(o)) {
|
|
804
|
+
l = !0;
|
|
790
805
|
break;
|
|
791
806
|
}
|
|
792
|
-
if (!
|
|
807
|
+
if (!l) continue;
|
|
793
808
|
}
|
|
794
809
|
}
|
|
795
810
|
r.push(n.id);
|
|
@@ -803,8 +818,8 @@ class Ot extends st {
|
|
|
803
818
|
* series without stacked concepts (Candlestick, single-layer Line/Bar).
|
|
804
819
|
* Returns `null` for unknown ids or empty series.
|
|
805
820
|
*/
|
|
806
|
-
getStackedLastValue(
|
|
807
|
-
return
|
|
821
|
+
getStackedLastValue(t) {
|
|
822
|
+
return X(t, this.#t, this.#e.getAnimationState().xRange);
|
|
808
823
|
}
|
|
809
824
|
/**
|
|
810
825
|
* Per-layer last snapshots with each layer's own `time`. Returns `null`
|
|
@@ -812,8 +827,8 @@ class Ot extends st {
|
|
|
812
827
|
* overlay components that must display every layer in last-mode even
|
|
813
828
|
* when layers advance at different rates (ragged streams).
|
|
814
829
|
*/
|
|
815
|
-
getLayerLastSnapshots(
|
|
816
|
-
return this.#t.find((
|
|
830
|
+
getLayerLastSnapshots(t) {
|
|
831
|
+
return this.#t.find((i) => i.id === t)?.renderer.getLayerLastSnapshots?.() ?? null;
|
|
817
832
|
}
|
|
818
833
|
/**
|
|
819
834
|
* Shallow view of the internal series entries so unit tests can introspect
|
|
@@ -823,20 +838,20 @@ class Ot extends st {
|
|
|
823
838
|
* @internal
|
|
824
839
|
*/
|
|
825
840
|
listSeriesForTest() {
|
|
826
|
-
return this.#t.map((
|
|
841
|
+
return this.#t.map((t) => ({ id: t.id, renderer: t.renderer }));
|
|
827
842
|
}
|
|
828
843
|
/** Get the primary display color for a series. */
|
|
829
|
-
getSeriesColor(
|
|
830
|
-
const
|
|
831
|
-
return
|
|
844
|
+
getSeriesColor(t) {
|
|
845
|
+
const i = this.#t.find((e) => e.id === t);
|
|
846
|
+
return i ? i.renderer.getLayerColors()[0] ?? this.#r.line.color : null;
|
|
832
847
|
}
|
|
833
848
|
/** The series' primary name (first layer). Used for single-layer overlays. */
|
|
834
|
-
getSeriesLabel(
|
|
835
|
-
return this.#t.find((
|
|
849
|
+
getSeriesLabel(t) {
|
|
850
|
+
return this.#t.find((i) => i.id === t)?.labels?.[0];
|
|
836
851
|
}
|
|
837
852
|
/** The display name of one layer, or `undefined` when that layer is unnamed. */
|
|
838
|
-
getLayerLabel(
|
|
839
|
-
return this.#t.find((e) => e.id ===
|
|
853
|
+
getLayerLabel(t, i) {
|
|
854
|
+
return this.#t.find((e) => e.id === t)?.labels?.[i];
|
|
840
855
|
}
|
|
841
856
|
/**
|
|
842
857
|
* Set every layer's display name at once (index = layer). Drives the tooltip,
|
|
@@ -844,9 +859,9 @@ class Ot extends st {
|
|
|
844
859
|
* per-layer labels in `data` change. No-ops (and skips the overlay bump) when
|
|
845
860
|
* the labels are unchanged, so it's safe to call on every data tick.
|
|
846
861
|
*/
|
|
847
|
-
setSeriesLabels(
|
|
848
|
-
const e = this.#t.find((s) => s.id ===
|
|
849
|
-
!e || I(e.labels,
|
|
862
|
+
setSeriesLabels(t, i) {
|
|
863
|
+
const e = this.#t.find((s) => s.id === t);
|
|
864
|
+
!e || I(e.labels, i) || (e.labels = i, this.#b());
|
|
850
865
|
}
|
|
851
866
|
/**
|
|
852
867
|
* Set the per-layer color overrides carried in `data` (sparse — `undefined`
|
|
@@ -855,44 +870,44 @@ class Ot extends st {
|
|
|
855
870
|
* the renderer, which keeps them separate from the theme base so a theme swap
|
|
856
871
|
* preserves them. Called by the framework wrappers; no-ops when unchanged.
|
|
857
872
|
*/
|
|
858
|
-
setLayerColors(
|
|
859
|
-
const e = this.#t.find((s) => s.id ===
|
|
860
|
-
!e || ft(e.colorOverrides,
|
|
873
|
+
setLayerColors(t, i) {
|
|
874
|
+
const e = this.#t.find((s) => s.id === t);
|
|
875
|
+
!e || ft(e.colorOverrides, i) || (e.colorOverrides = i, e.renderer.setColorOverrides?.(i), this.#i.markDirty(), this.#b());
|
|
861
876
|
}
|
|
862
877
|
/** Get per-layer colors for a series. Returns null for single-layer non-bar/line series. */
|
|
863
|
-
getSeriesLayers(
|
|
864
|
-
const
|
|
865
|
-
if (!
|
|
866
|
-
const e =
|
|
878
|
+
getSeriesLayers(t) {
|
|
879
|
+
const i = this.#t.find((r) => r.id === t);
|
|
880
|
+
if (!i) return null;
|
|
881
|
+
const e = i.renderer.getLayerCount();
|
|
867
882
|
if (e <= 1) return null;
|
|
868
|
-
const s =
|
|
883
|
+
const s = i.renderer.getLayerColors();
|
|
869
884
|
return Array.from({ length: e }, (r, n) => ({ color: s[n % s.length] }));
|
|
870
885
|
}
|
|
871
886
|
/** Get all slices with computed colors and percentages. Returns null for series without slice data (e.g. candlestick, line, bar). */
|
|
872
|
-
getSliceInfo(
|
|
873
|
-
return this.#t.find((
|
|
887
|
+
getSliceInfo(t) {
|
|
888
|
+
return this.#t.find((i) => i.id === t)?.renderer.getSliceInfo?.(this.#r) ?? null;
|
|
874
889
|
}
|
|
875
890
|
/** Get hover info (label/value/percent/color) for the currently hovered element, or null. */
|
|
876
|
-
getHoverInfo(
|
|
877
|
-
return this.#t.find((
|
|
891
|
+
getHoverInfo(t) {
|
|
892
|
+
return this.#t.find((i) => i.id === t)?.renderer.getHoverInfo?.(this.#r) ?? null;
|
|
878
893
|
}
|
|
879
894
|
/** Apply a new theme and update series colors where appropriate. */
|
|
880
|
-
setTheme(
|
|
881
|
-
const
|
|
882
|
-
this.#r =
|
|
895
|
+
setTheme(t) {
|
|
896
|
+
const i = this.#r;
|
|
897
|
+
this.#r = t;
|
|
883
898
|
for (const e of this.#t)
|
|
884
|
-
e.renderer.applyTheme(
|
|
885
|
-
this.#i.markDirty(), this.#
|
|
899
|
+
e.renderer.applyTheme(t, i);
|
|
900
|
+
this.#i.markDirty(), this.#b();
|
|
886
901
|
}
|
|
887
902
|
getTheme() {
|
|
888
903
|
return this.#r;
|
|
889
904
|
}
|
|
890
905
|
/** Update axis configuration and re-render. */
|
|
891
|
-
setAxis(
|
|
892
|
-
const
|
|
893
|
-
this.#I =
|
|
906
|
+
setAxis(t) {
|
|
907
|
+
const i = this.yAxisWidth, e = this.xAxisHeight;
|
|
908
|
+
this.#I = t, this.#h = { min: t.y?.min, max: t.y?.max }, this.#u = !0;
|
|
894
909
|
const s = performance.now();
|
|
895
|
-
this.#e.onAxisReconfig(s), this.#
|
|
910
|
+
this.#e.onAxisReconfig(s), this.#p(s), (this.yAxisWidth !== i || this.xAxisHeight !== e) && this.syncScales(), this.#i.markDirty();
|
|
896
911
|
}
|
|
897
912
|
/**
|
|
898
913
|
* Apply label-density knobs driven by `<YAxis labelCount=… minLabelSpacing=…>`
|
|
@@ -900,29 +915,29 @@ class Ot extends st {
|
|
|
900
915
|
* Y-animation reset. Triggers a viewportChange + repaint so static charts
|
|
901
916
|
* react without waiting for pan/zoom.
|
|
902
917
|
*/
|
|
903
|
-
setYAxisLabelDensity(
|
|
904
|
-
let
|
|
905
|
-
"labelCount" in
|
|
918
|
+
setYAxisLabelDensity(t) {
|
|
919
|
+
let i = !1;
|
|
920
|
+
"labelCount" in t && (this.yScale.setLabelCount(t.labelCount ?? null), i = !0), "minLabelSpacing" in t && (this.yScale.setMinSpacing(t.minLabelSpacing ?? null), i = !0), i && (this.syncScales(), this.emit("viewportChange"), this.#i.markDirty());
|
|
906
921
|
}
|
|
907
922
|
/** Label-density knobs for the time axis — mirror of {@link setYAxisLabelDensity}. */
|
|
908
|
-
setTimeAxisLabelDensity(
|
|
909
|
-
let
|
|
910
|
-
"labelCount" in
|
|
923
|
+
setTimeAxisLabelDensity(t) {
|
|
924
|
+
let i = !1;
|
|
925
|
+
"labelCount" in t && (this.timeScale.setLabelCount(t.labelCount ?? null), i = !0), "minLabelSpacing" in t && (this.timeScale.setMinSpacing(t.minLabelSpacing ?? null), i = !0), i && (this.syncScales(), this.emit("viewportChange"), this.#i.markDirty());
|
|
911
926
|
}
|
|
912
927
|
getMediaSize() {
|
|
913
928
|
return this.#n.size.media;
|
|
914
929
|
}
|
|
915
930
|
/** Returns layout metrics for the chart area, Y axis, and time axis. */
|
|
916
931
|
getLayout() {
|
|
917
|
-
const
|
|
932
|
+
const t = this.#n.size.media, i = this.yAxisWidth, e = this.xAxisHeight;
|
|
918
933
|
return {
|
|
919
|
-
chartArea: { x: 0, y: 0, width:
|
|
920
|
-
yAxisWidth:
|
|
934
|
+
chartArea: { x: 0, y: 0, width: t.width - i, height: t.height - e },
|
|
935
|
+
yAxisWidth: i,
|
|
921
936
|
xAxisHeight: e
|
|
922
937
|
};
|
|
923
938
|
}
|
|
924
939
|
getDataInterval() {
|
|
925
|
-
return this.#
|
|
940
|
+
return this.#a;
|
|
926
941
|
}
|
|
927
942
|
/**
|
|
928
943
|
* Update viewport padding at runtime. Refits the visible time range to
|
|
@@ -932,27 +947,27 @@ class Ot extends st {
|
|
|
932
947
|
* a wrapper re-applies padding reactively (e.g. in response to a Title /
|
|
933
948
|
* InfoBar ResizeObserver).
|
|
934
949
|
*/
|
|
935
|
-
setPadding(
|
|
936
|
-
const
|
|
937
|
-
this.#
|
|
938
|
-
const e = this.#
|
|
950
|
+
setPadding(t) {
|
|
951
|
+
const i = this.#c;
|
|
952
|
+
this.#c = D(t);
|
|
953
|
+
const e = this.#c, s = !T(i.left, e.left) || !T(i.right, e.right), r = i.top !== e.top || i.bottom !== e.bottom;
|
|
939
954
|
if (s) {
|
|
940
|
-
const { first: n, last:
|
|
941
|
-
if (n !== void 0 &&
|
|
942
|
-
const
|
|
943
|
-
this.#
|
|
955
|
+
const { first: n, last: a } = this.getDataBounds();
|
|
956
|
+
if (n !== void 0 && a !== void 0) {
|
|
957
|
+
const h = this.#n.size.media.width - this.yAxisWidth;
|
|
958
|
+
this.#R(n, a, h);
|
|
944
959
|
}
|
|
945
960
|
}
|
|
946
961
|
if (s || r) {
|
|
947
962
|
this.#u = !0;
|
|
948
963
|
const n = performance.now();
|
|
949
|
-
this.#e.onAxisReconfig(n), this.#
|
|
964
|
+
this.#e.onAxisReconfig(n), this.#p(n);
|
|
950
965
|
}
|
|
951
966
|
this.syncScales(), this.#i.markDirty(), r && this.emit("viewportChange");
|
|
952
967
|
}
|
|
953
968
|
/** Show or hide the background grid. Takes effect on the next render frame. */
|
|
954
|
-
setGrid(
|
|
955
|
-
this.#E =
|
|
969
|
+
setGrid(t) {
|
|
970
|
+
this.#E = t.visible, this.#i.markDirty();
|
|
956
971
|
}
|
|
957
972
|
/**
|
|
958
973
|
* Set the visual state for one side of the chart. Typically called in
|
|
@@ -963,12 +978,12 @@ class Ot extends st {
|
|
|
963
978
|
* The state persists until replaced. `has-more` is accepted for API
|
|
964
979
|
* symmetry and currently renders identically to `idle`.
|
|
965
980
|
*/
|
|
966
|
-
setEdgeState(
|
|
967
|
-
this.#
|
|
981
|
+
setEdgeState(t, i) {
|
|
982
|
+
this.#x[t] !== i && (this.#x[t] = i, this.#l.markDirty());
|
|
968
983
|
}
|
|
969
984
|
/** Read the current host-declared state for a given edge. */
|
|
970
|
-
getEdgeState(
|
|
971
|
-
return this.#
|
|
985
|
+
getEdgeState(t) {
|
|
986
|
+
return this.#x[t];
|
|
972
987
|
}
|
|
973
988
|
/**
|
|
974
989
|
* Notify chart that a YLabel overlay is mounted / unmounted. Currently a
|
|
@@ -976,73 +991,113 @@ class Ot extends st {
|
|
|
976
991
|
* chart area to make room for the badge.
|
|
977
992
|
* TODO: implement reflow.
|
|
978
993
|
*/
|
|
979
|
-
setYLabel(
|
|
994
|
+
setYLabel(t) {
|
|
980
995
|
}
|
|
981
996
|
/**
|
|
982
997
|
* Dispatch a crosshair move to every renderer that supports spatial hover
|
|
983
998
|
* (currently: pie). Any change in hover index schedules a main-layer redraw.
|
|
984
999
|
*/
|
|
985
|
-
#
|
|
986
|
-
const
|
|
987
|
-
top: e.top *
|
|
988
|
-
bottom: e.bottom *
|
|
1000
|
+
#_(t) {
|
|
1001
|
+
const i = this.#n.size, e = this.#c, s = {
|
|
1002
|
+
top: e.top * i.verticalPixelRatio,
|
|
1003
|
+
bottom: e.bottom * i.verticalPixelRatio
|
|
989
1004
|
};
|
|
990
1005
|
let r = !1;
|
|
991
1006
|
for (const n of this.#t) {
|
|
992
1007
|
if (!n.renderer.hitTest || !n.renderer.setHoverIndex) continue;
|
|
993
|
-
let
|
|
994
|
-
if (
|
|
995
|
-
const
|
|
996
|
-
|
|
1008
|
+
let a = -1;
|
|
1009
|
+
if (t) {
|
|
1010
|
+
const h = t.mediaX * i.horizontalPixelRatio, l = t.mediaY * i.verticalPixelRatio;
|
|
1011
|
+
a = n.renderer.hitTest(h, l, i.bitmap.width, i.bitmap.height, s);
|
|
997
1012
|
}
|
|
998
|
-
n.renderer.setHoverIndex(
|
|
1013
|
+
n.renderer.setHoverIndex(a) && (r = !0);
|
|
999
1014
|
}
|
|
1000
1015
|
r && this.#i.markDirty();
|
|
1001
1016
|
}
|
|
1017
|
+
/**
|
|
1018
|
+
* Re-derive the crosshair after the viewport moved beneath it. A streaming
|
|
1019
|
+
* auto-scroll, wheel zoom, or animated Y re-fit shifts the pixel↔data
|
|
1020
|
+
* mapping while the cursor stays put — without this, the stored position
|
|
1021
|
+
* goes stale: the tooltip freezes on the old time/values while the data
|
|
1022
|
+
* (and any snap dots anchored to it) slides away underneath.
|
|
1023
|
+
*
|
|
1024
|
+
* Which half of the position is truth depends on {@link #crosshairAnchor}:
|
|
1025
|
+
* a pointer-anchored crosshair stays under the cursor and re-resolves
|
|
1026
|
+
* `time`/`y`; a time-anchored one keeps `time`/`y` and re-projects the
|
|
1027
|
+
* pixel position so it tracks its data point across the screen. A
|
|
1028
|
+
* time-anchored point whose time scrolls out of the visible range clears
|
|
1029
|
+
* the crosshair entirely (mirroring pointer-leave) — otherwise the
|
|
1030
|
+
* edge-clamped tooltip would pin to the chart border, live-updating for
|
|
1031
|
+
* an invisible point.
|
|
1032
|
+
*
|
|
1033
|
+
* Called once per painted main frame (after `syncScales`). When the
|
|
1034
|
+
* viewport didn't move, the re-derived values are bit-identical and the
|
|
1035
|
+
* method exits without emitting.
|
|
1036
|
+
*/
|
|
1037
|
+
#yt() {
|
|
1038
|
+
const t = this.#d;
|
|
1039
|
+
if (t === null) return;
|
|
1040
|
+
const { from: i, to: e } = this.timeScale.getRange(), s = this.yScale.getRange(), r = Math.abs(e - i) * 1e-9, n = Math.abs(s.max - s.min) * 1e-9, a = 1e-6;
|
|
1041
|
+
let h;
|
|
1042
|
+
if (this.#F === "pointer") {
|
|
1043
|
+
const l = this.timeScale.xToTime(t.mediaX), o = this.yScale.yToValue(t.mediaY);
|
|
1044
|
+
if (!Number.isFinite(l) || !Number.isFinite(o) || Math.abs(l - t.time) <= r && Math.abs(o - t.y) <= n) return;
|
|
1045
|
+
h = { mediaX: t.mediaX, mediaY: t.mediaY, time: l, y: o };
|
|
1046
|
+
} else {
|
|
1047
|
+
if (t.time < i || t.time > e) {
|
|
1048
|
+
this.#d = null, this.#l.markDirty(), this.emit("crosshairMove", null), this.#_(null);
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
1051
|
+
const l = this.timeScale.timeToX(t.time), o = this.yScale.valueToY(t.y);
|
|
1052
|
+
if (!Number.isFinite(l) || !Number.isFinite(o) || Math.abs(l - t.mediaX) <= a && Math.abs(o - t.mediaY) <= a) return;
|
|
1053
|
+
h = { mediaX: l, mediaY: o, time: t.time, y: t.y };
|
|
1054
|
+
}
|
|
1055
|
+
this.#d = h, this.#l.markDirty(), this.emit("crosshairMove", h), this.#_(h);
|
|
1056
|
+
}
|
|
1002
1057
|
/** Tear down the chart: cancel animations, remove listeners, and detach the canvas. */
|
|
1003
1058
|
destroy() {
|
|
1004
|
-
for (const
|
|
1005
|
-
this.#t = [], this.#
|
|
1059
|
+
for (const t of this.#t) t.renderer.dispose();
|
|
1060
|
+
this.#t = [], this.#m = [], this.#v = [], this.#g = [], this.#A = null, this.#i.destroy(), this.#l.destroy(), this.#W?.destroy(), this.#n.destroy(), this.#X?.destroy(), this.#X = null, this.#B && this.#S?.destroy(), this.#S = null, this.#B = !1, this.removeAllListeners();
|
|
1006
1061
|
}
|
|
1007
1062
|
/** The attached performance monitor, or `null` when instrumentation is disabled. */
|
|
1008
1063
|
getPerfMonitor() {
|
|
1009
|
-
return this.#
|
|
1064
|
+
return this.#S;
|
|
1010
1065
|
}
|
|
1011
1066
|
/** Compute the earliest and latest timestamps across all series. */
|
|
1012
1067
|
getDataBounds() {
|
|
1013
|
-
let
|
|
1068
|
+
let t, i;
|
|
1014
1069
|
for (const e of this.#t) {
|
|
1015
1070
|
if (!e.visible || e.renderer.kind === "pie") continue;
|
|
1016
1071
|
const s = e.renderer.getTimeBounds();
|
|
1017
|
-
s !== null && ((
|
|
1072
|
+
s !== null && ((t === void 0 || s.first < t) && (t = s.first), (i === void 0 || s.last > i) && (i = s.last));
|
|
1018
1073
|
}
|
|
1019
|
-
return { first:
|
|
1074
|
+
return { first: t, last: i };
|
|
1020
1075
|
}
|
|
1021
1076
|
/** Set by `setSeriesData`/bulk-replace paths so the next `onDataChanged`
|
|
1022
1077
|
* snaps the Y range (instead of easing) and `yScale.getRange()` reflects
|
|
1023
1078
|
* the new domain synchronously. Cleared by `onDataChanged` after each run. */
|
|
1024
|
-
|
|
1079
|
+
#$ = !1;
|
|
1025
1080
|
onDataChanged() {
|
|
1026
1081
|
if (this.#w > 0) {
|
|
1027
|
-
this
|
|
1082
|
+
this.#O = !0;
|
|
1028
1083
|
return;
|
|
1029
1084
|
}
|
|
1030
1085
|
this.updateDataInterval();
|
|
1031
|
-
const { first:
|
|
1032
|
-
|
|
1033
|
-
const e = this
|
|
1034
|
-
this
|
|
1035
|
-
const s = !this.#u &&
|
|
1036
|
-
e && (this.yScale.tickTracker.reset(), this.timeScale.tickTracker.reset()), this.#
|
|
1086
|
+
const { first: t, last: i } = this.getDataBounds();
|
|
1087
|
+
t !== void 0 && (this.#k = t), i !== void 0 && (this.#P = this.#s, this.#s = i);
|
|
1088
|
+
const e = this.#$;
|
|
1089
|
+
this.#$ = !1;
|
|
1090
|
+
const s = !this.#u && t !== void 0;
|
|
1091
|
+
e && (this.yScale.tickTracker.reset(), this.timeScale.tickTracker.reset()), this.#tt = e;
|
|
1037
1092
|
const r = performance.now();
|
|
1038
|
-
e || s ? (this.#D.seed(r), this.#e.onDataReplaced(r)) : this.#e.onPointAppended(r), this.#e.lastXTarget !== null && (this.#
|
|
1093
|
+
e || s ? (this.#D.seed(r), this.#e.onDataReplaced(r)) : this.#e.onPointAppended(r), this.#e.lastXTarget !== null && (this.#M = !0), t !== void 0 && (this.#u = !0), this.#p(r), this.syncScales(), s ? this.#G() : this.#i.markDirty(), this.emit("dataUpdate"), this.#b();
|
|
1039
1094
|
}
|
|
1040
1095
|
updateDataInterval() {
|
|
1041
|
-
for (const
|
|
1042
|
-
if (
|
|
1043
|
-
const
|
|
1044
|
-
if (
|
|
1045
|
-
this.#
|
|
1096
|
+
for (const t of this.#t) {
|
|
1097
|
+
if (t.renderer.kind === "pie") continue;
|
|
1098
|
+
const i = t.renderer.sampleTimes(20);
|
|
1099
|
+
if (i.length >= 2) {
|
|
1100
|
+
this.#a = lt(i);
|
|
1046
1101
|
break;
|
|
1047
1102
|
}
|
|
1048
1103
|
}
|
|
@@ -1058,22 +1113,22 @@ class Ot extends st {
|
|
|
1058
1113
|
* range — without this guard the engine would ease from `{0,0}` into
|
|
1059
1114
|
* the new target and the very first frame would render with a
|
|
1060
1115
|
* zero-width X domain. */
|
|
1061
|
-
#
|
|
1116
|
+
#M = !1;
|
|
1062
1117
|
/**
|
|
1063
1118
|
* One-shot `setVisibleRange` argument applied on the first data arrival.
|
|
1064
1119
|
* Captured at chart construction from {@link ChartOptions.viewport.initialRange};
|
|
1065
1120
|
* cleared the moment it's consumed so subsequent data swaps don't snap
|
|
1066
1121
|
* back to the initial window.
|
|
1067
1122
|
*/
|
|
1068
|
-
#
|
|
1123
|
+
#N;
|
|
1069
1124
|
/** Last Y bounds the engine resolved into {@link AnimationState.yRange}.
|
|
1070
1125
|
* Used by {@link renderMain} to detect Y movement frame-to-frame so
|
|
1071
1126
|
* `viewportChange` only emits when the Y range actually shifted — DOM
|
|
1072
1127
|
* axis labels subscribe to that event and re-renders on every tick are
|
|
1073
1128
|
* expensive. Initialised to NaN so the first real Y emit always passes
|
|
1074
1129
|
* the change check. */
|
|
1075
|
-
#
|
|
1076
|
-
#
|
|
1130
|
+
#U = Number.NaN;
|
|
1131
|
+
#Z = Number.NaN;
|
|
1077
1132
|
/**
|
|
1078
1133
|
* Sample the visible data window and return the resolved Y bounds, or
|
|
1079
1134
|
* `null` when no series has data inside the X destination range.
|
|
@@ -1082,11 +1137,11 @@ class Ot extends st {
|
|
|
1082
1137
|
* converge together. Clears {@link #yInited} on empty so the next emit
|
|
1083
1138
|
* with valid data lands as an instant snap.
|
|
1084
1139
|
*/
|
|
1085
|
-
#
|
|
1086
|
-
const
|
|
1140
|
+
#nt(t) {
|
|
1141
|
+
const i = t ?? this.#o, e = this.#h.min !== void 0 && this.#h.min !== "auto" && typeof this.#h.min != "number" || this.#h.max !== void 0 && this.#h.max !== "auto" && typeof this.#h.max != "number" ? [] : null, s = q(i, this.#t, e);
|
|
1087
1142
|
if (s === null)
|
|
1088
1143
|
return this.#u = !1, null;
|
|
1089
|
-
const r =
|
|
1144
|
+
const r = M(this.#h.min, s.min, s.max, e ?? [], "min"), n = M(this.#h.max, s.max, s.min, e ?? [], "max");
|
|
1090
1145
|
return { min: r, max: n };
|
|
1091
1146
|
}
|
|
1092
1147
|
/**
|
|
@@ -1107,24 +1162,24 @@ class Ot extends st {
|
|
|
1107
1162
|
* - **autoscroll off** → `null` (no X retarget; user has panned away
|
|
1108
1163
|
* from the tail).
|
|
1109
1164
|
*/
|
|
1110
|
-
#
|
|
1165
|
+
#pt() {
|
|
1111
1166
|
if (this.#k === null || this.#s === null) return null;
|
|
1112
|
-
const { from:
|
|
1167
|
+
const { from: t, to: i } = this.#o, e = t === 0 && i === 0, s = this.#n.size.media.width - this.yAxisWidth;
|
|
1113
1168
|
if (e)
|
|
1114
|
-
return this.#
|
|
1115
|
-
if (!this.#
|
|
1116
|
-
if (this.#
|
|
1117
|
-
return this.#
|
|
1169
|
+
return this.#R(this.#k, this.#s, s), this.#N !== void 0 && (this.setVisibleRange(this.#N), this.#N = void 0), this.#o;
|
|
1170
|
+
if (!this.#y) return null;
|
|
1171
|
+
if (this.#tt)
|
|
1172
|
+
return this.#R(this.#k, this.#s, s), this.#o;
|
|
1118
1173
|
const r = j({
|
|
1119
1174
|
currentLogical: this.#o,
|
|
1120
1175
|
lastTime: this.#s,
|
|
1121
1176
|
prevDataEnd: this.#P,
|
|
1122
|
-
dataInterval: this.#
|
|
1123
|
-
paddingRight: this.#
|
|
1177
|
+
dataInterval: this.#a,
|
|
1178
|
+
paddingRight: this.#c.right,
|
|
1124
1179
|
chartWidth: s,
|
|
1125
1180
|
holdUntilFilled: this.#L
|
|
1126
1181
|
});
|
|
1127
|
-
return r.releaseHold && (this.#L = !1), r.reengageAutoScroll && (this.#
|
|
1182
|
+
return r.releaseHold && (this.#L = !1), r.reengageAutoScroll && (this.#y = !0), r.newLogical === null ? null : (this.#D.observe(performance.now()), this.#e.setXSettleMs(this.#D.pickSettleMs(this.#f.axis.x.settleMs)), this.#T(r.newLogical, { emitChange: !1, skipValidation: !0 }), this.#P = this.#s, r.newLogical);
|
|
1128
1183
|
}
|
|
1129
1184
|
/**
|
|
1130
1185
|
* Fit the viewport's logical X range around a data span. Mirrors the
|
|
@@ -1132,16 +1187,16 @@ class Ot extends st {
|
|
|
1132
1187
|
* forces autoscroll on (data tail is in the new window by construction),
|
|
1133
1188
|
* commits the new range via `viewport.setRange` (which emits `change`).
|
|
1134
1189
|
*/
|
|
1135
|
-
#
|
|
1136
|
-
const s = this.#
|
|
1137
|
-
firstTime:
|
|
1138
|
-
lastTime:
|
|
1139
|
-
dataInterval: this.#
|
|
1140
|
-
maxVisibleBars: this.#
|
|
1190
|
+
#R(t, i, e) {
|
|
1191
|
+
const s = this.#c, r = O({
|
|
1192
|
+
firstTime: t,
|
|
1193
|
+
lastTime: i,
|
|
1194
|
+
dataInterval: this.#a,
|
|
1195
|
+
maxVisibleBars: this.#it,
|
|
1141
1196
|
chartWidth: e,
|
|
1142
1197
|
padding: { left: s.left, right: s.right }
|
|
1143
1198
|
});
|
|
1144
|
-
this.#L = !1, this.#
|
|
1199
|
+
this.#L = !1, this.#y = !0, this.#T(r, { emitChange: !0 });
|
|
1145
1200
|
}
|
|
1146
1201
|
/**
|
|
1147
1202
|
* Commit a programmatic zoom — `fitContent` or `setVisibleRange`. Reads
|
|
@@ -1157,15 +1212,15 @@ class Ot extends st {
|
|
|
1157
1212
|
* passes `xEase: true` so X eases instead — viewport and axis converge
|
|
1158
1213
|
* on the same frame.
|
|
1159
1214
|
*/
|
|
1160
|
-
#
|
|
1161
|
-
const
|
|
1162
|
-
if (
|
|
1215
|
+
#H(t) {
|
|
1216
|
+
const i = this.#o;
|
|
1217
|
+
if (i.to <= i.from) return;
|
|
1163
1218
|
const e = performance.now();
|
|
1164
|
-
if (!this.#
|
|
1165
|
-
this.#
|
|
1219
|
+
if (!this.#M || !this.#u) {
|
|
1220
|
+
this.#at(e);
|
|
1166
1221
|
return;
|
|
1167
1222
|
}
|
|
1168
|
-
this.#e.onProgrammaticZoom({ xTarget:
|
|
1223
|
+
this.#e.onProgrammaticZoom({ xTarget: i, xEase: t?.xEase }, e), this.#p(e);
|
|
1169
1224
|
}
|
|
1170
1225
|
/**
|
|
1171
1226
|
* Commit the current logical X to the engine on the *gesture* profile:
|
|
@@ -1179,15 +1234,15 @@ class Ot extends st {
|
|
|
1179
1234
|
* instead of lagging on the long sticky contract. First paint still snaps
|
|
1180
1235
|
* (same as the programmatic path) so synchronous readers see final values.
|
|
1181
1236
|
*/
|
|
1182
|
-
#
|
|
1183
|
-
const
|
|
1184
|
-
if (
|
|
1185
|
-
const
|
|
1186
|
-
if (!this.#
|
|
1187
|
-
this.#
|
|
1237
|
+
#vt() {
|
|
1238
|
+
const t = this.#o;
|
|
1239
|
+
if (t.to <= t.from) return;
|
|
1240
|
+
const i = performance.now();
|
|
1241
|
+
if (!this.#M || !this.#u) {
|
|
1242
|
+
this.#at(i);
|
|
1188
1243
|
return;
|
|
1189
1244
|
}
|
|
1190
|
-
this.#e.onPanZoom({ xTarget:
|
|
1245
|
+
this.#e.onPanZoom({ xTarget: t, yAuto: !0 }, i), this.#p(i);
|
|
1191
1246
|
}
|
|
1192
1247
|
/**
|
|
1193
1248
|
* First-paint snap: land both axes on the current logical X immediately
|
|
@@ -1196,9 +1251,9 @@ class Ot extends st {
|
|
|
1196
1251
|
* commits — both must snap before the chart has painted once so a
|
|
1197
1252
|
* synchronous `getYRange()` / `getVisibleRange()` read sees final values.
|
|
1198
1253
|
*/
|
|
1199
|
-
#
|
|
1200
|
-
const
|
|
1201
|
-
this.#
|
|
1254
|
+
#at(t) {
|
|
1255
|
+
const i = this.#o, e = this.#nt(i);
|
|
1256
|
+
this.#M = !0, e !== null && (this.#u = !0), this.#e.snap({ x: i, y: e ?? void 0 }, t), this.#p(t);
|
|
1202
1257
|
}
|
|
1203
1258
|
/**
|
|
1204
1259
|
* Validate and commit a new logical X range. Mirrors the old
|
|
@@ -1218,9 +1273,9 @@ class Ot extends st {
|
|
|
1218
1273
|
* see `setVisibleRange`'s gesture path, where a per-frame echo would
|
|
1219
1274
|
* otherwise re-arm and stall the in-flight Y ease.
|
|
1220
1275
|
*/
|
|
1221
|
-
#
|
|
1222
|
-
const { from: e, to: s } =
|
|
1223
|
-
return !Number.isFinite(e) || !Number.isFinite(s) || s <= e || !
|
|
1276
|
+
#T(t, i) {
|
|
1277
|
+
const { from: e, to: s } = t;
|
|
1278
|
+
return !Number.isFinite(e) || !Number.isFinite(s) || s <= e || !i.skipValidation && (s - e) / this.#a < 2 || this.#o.from === e && this.#o.to === s ? !1 : (this.#o = { from: e, to: s }, i.emitChange && (this.syncScales(), this.#i?.markDirty(), this.emit("viewportChange")), !0);
|
|
1224
1279
|
}
|
|
1225
1280
|
/**
|
|
1226
1281
|
* Apply pixel-padding to the engine's raw Y target and store the
|
|
@@ -1228,11 +1283,11 @@ class Ot extends st {
|
|
|
1228
1283
|
* symmetric pad top / bottom from `#padding`, suppressed on the side
|
|
1229
1284
|
* that has an explicit (fixed) axis bound.
|
|
1230
1285
|
*/
|
|
1231
|
-
#
|
|
1232
|
-
const n =
|
|
1286
|
+
#ht(t, i, e, s, r) {
|
|
1287
|
+
const n = i - t, a = e > 0 ? this.#c.top / e * n : 0, h = e > 0 ? this.#c.bottom / e * n : 0;
|
|
1233
1288
|
this.#V = {
|
|
1234
|
-
min: s ?
|
|
1235
|
-
max: r ?
|
|
1289
|
+
min: s ? t : t - h,
|
|
1290
|
+
max: r ? i : i + a
|
|
1236
1291
|
};
|
|
1237
1292
|
}
|
|
1238
1293
|
/**
|
|
@@ -1245,18 +1300,18 @@ class Ot extends st {
|
|
|
1245
1300
|
* as a gesture for visual easing. Public so consumers can drive pan
|
|
1246
1301
|
* programmatically.
|
|
1247
1302
|
*/
|
|
1248
|
-
pan(
|
|
1249
|
-
|
|
1303
|
+
pan(t, i = this.#z) {
|
|
1304
|
+
i > 0 && (this.#z = i), this.#L = !1;
|
|
1250
1305
|
const e = U({
|
|
1251
1306
|
currentLogical: this.#o,
|
|
1252
|
-
timeDelta:
|
|
1253
|
-
chartWidth:
|
|
1254
|
-
dataInterval: this.#
|
|
1255
|
-
padding: { left: this.#
|
|
1307
|
+
timeDelta: t,
|
|
1308
|
+
chartWidth: i,
|
|
1309
|
+
dataInterval: this.#a,
|
|
1310
|
+
padding: { left: this.#c.left, right: this.#c.right },
|
|
1256
1311
|
dataStart: this.#k,
|
|
1257
1312
|
dataEnd: this.#s
|
|
1258
1313
|
});
|
|
1259
|
-
e.newLogical !== null && (this.#
|
|
1314
|
+
e.newLogical !== null && (this.#y = !e.autoScrollOff, this.#T(e.newLogical, { emitChange: !0 }), e.edgeReached !== null && (this.#J[e.edgeReached.side] = e.edgeReached.boundaryTime, this.#K?.(e.edgeReached)), this.#D.pause(), this.#ot());
|
|
1260
1315
|
}
|
|
1261
1316
|
/**
|
|
1262
1317
|
* Zoom around a time anchor. `factor < 1` zooms in, `> 1` zooms out.
|
|
@@ -1273,37 +1328,37 @@ class Ot extends st {
|
|
|
1273
1328
|
* and produce a visible jump). Cursor-anchored zoom is intentionally
|
|
1274
1329
|
* sacrificed in follow-live mode — pan first to inspect history.
|
|
1275
1330
|
*/
|
|
1276
|
-
zoomAt(
|
|
1331
|
+
zoomAt(t, i, e = this.#z) {
|
|
1277
1332
|
e > 0 && (this.#z = e), this.#L = !1;
|
|
1278
1333
|
const s = Z({
|
|
1279
1334
|
currentLogical: this.#o,
|
|
1280
|
-
centerTime:
|
|
1281
|
-
factor:
|
|
1335
|
+
centerTime: t,
|
|
1336
|
+
factor: i,
|
|
1282
1337
|
chartWidth: e,
|
|
1283
|
-
dataInterval: this.#
|
|
1284
|
-
padding: { left: this.#
|
|
1338
|
+
dataInterval: this.#a,
|
|
1339
|
+
padding: { left: this.#c.left, right: this.#c.right },
|
|
1285
1340
|
dataStart: this.#k,
|
|
1286
1341
|
dataEnd: this.#s
|
|
1287
1342
|
});
|
|
1288
1343
|
if (s.newLogical === null) return;
|
|
1289
1344
|
let r = s.newLogical;
|
|
1290
|
-
if (this.#
|
|
1291
|
-
const n = r.to - r.from,
|
|
1292
|
-
r = { from:
|
|
1345
|
+
if (this.#y && this.#s !== null) {
|
|
1346
|
+
const n = r.to - r.from, a = C(this.#c.right, n, this.#a, e), h = this.#s + a;
|
|
1347
|
+
r = { from: h - n, to: h }, this.#P = this.#s;
|
|
1293
1348
|
}
|
|
1294
|
-
this.#
|
|
1349
|
+
this.#T(r, { emitChange: !0 }), this.#D.pause(), this.#ot();
|
|
1295
1350
|
}
|
|
1296
1351
|
/**
|
|
1297
1352
|
* Common tail for `pan` / `zoomAt`: routes the just-committed `#logical`
|
|
1298
1353
|
* to the engine as a gesture so the X spring retargets and the Y spring
|
|
1299
1354
|
* eases over `animations.axis.y.gesture`.
|
|
1300
1355
|
*/
|
|
1301
|
-
#
|
|
1302
|
-
const
|
|
1303
|
-
if (
|
|
1304
|
-
this.#
|
|
1305
|
-
const
|
|
1306
|
-
this.#e.onPanZoom({ xTarget:
|
|
1356
|
+
#ot() {
|
|
1357
|
+
const t = this.#o;
|
|
1358
|
+
if (t.to <= t.from) return;
|
|
1359
|
+
this.#M = !0, this.#u = !0;
|
|
1360
|
+
const i = performance.now();
|
|
1361
|
+
this.#e.onPanZoom({ xTarget: t, yAuto: !0 }, i), this.#p(i);
|
|
1307
1362
|
}
|
|
1308
1363
|
/**
|
|
1309
1364
|
* Tick the engine at the given wall clock and push the resolved X / Y
|
|
@@ -1318,11 +1373,11 @@ class Ot extends st {
|
|
|
1318
1373
|
* `effectiveNow === startWall` and the prune step keeps zero-duration
|
|
1319
1374
|
* events long enough for the slot processor to snap them.
|
|
1320
1375
|
*/
|
|
1321
|
-
#
|
|
1322
|
-
const
|
|
1376
|
+
#p(t) {
|
|
1377
|
+
const i = this.#e.tick(t), e = this.#n.size;
|
|
1323
1378
|
if (e.media.width === 0 || e.media.height === 0) return;
|
|
1324
|
-
const s = this.#
|
|
1325
|
-
this.#
|
|
1379
|
+
const s = this.#h.min !== void 0 && this.#h.min !== "auto", r = this.#h.max !== void 0 && this.#h.max !== "auto", n = e.media.height - this.xAxisHeight;
|
|
1380
|
+
this.#ht(i.yRange.min, i.yRange.max, n, s, r), this.#U = i.yRange.min, this.#Z = i.yRange.max, this.syncScales();
|
|
1326
1381
|
}
|
|
1327
1382
|
/**
|
|
1328
1383
|
* Lightweight scale sync: updates timeScale/yScale from current viewport state
|
|
@@ -1330,79 +1385,79 @@ class Ot extends st {
|
|
|
1330
1385
|
* handler, so DOM axis components always read fresh coordinates on re-render.
|
|
1331
1386
|
*/
|
|
1332
1387
|
syncScales() {
|
|
1333
|
-
const
|
|
1334
|
-
if (
|
|
1335
|
-
const
|
|
1388
|
+
const t = this.#n.size;
|
|
1389
|
+
if (t.media.width === 0 || t.media.height === 0) return;
|
|
1390
|
+
const i = t.media.width - this.yAxisWidth, e = t.media.height - this.xAxisHeight;
|
|
1336
1391
|
this.timeScale.update(
|
|
1337
1392
|
this.#e.getAnimationState().xRange,
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
this.#
|
|
1341
|
-
), this.yScale.update(this.#V, e,
|
|
1393
|
+
i,
|
|
1394
|
+
t.horizontalPixelRatio,
|
|
1395
|
+
this.#a
|
|
1396
|
+
), this.yScale.update(this.#V, e, t.verticalPixelRatio);
|
|
1342
1397
|
}
|
|
1343
1398
|
/** Expensive: background, grid, all series. Only on data/viewport/resize change. */
|
|
1344
|
-
renderMain(
|
|
1345
|
-
const
|
|
1346
|
-
if (
|
|
1347
|
-
const e = typeof
|
|
1399
|
+
renderMain(t) {
|
|
1400
|
+
const i = this.#n.size;
|
|
1401
|
+
if (i.media.width === 0 || i.media.height === 0) return;
|
|
1402
|
+
const e = typeof t == "number" ? t : performance.now(), s = this.#e.tick(e);
|
|
1348
1403
|
s.animating && this.#i.markDirty();
|
|
1349
|
-
const r = s.yRange.min, n = s.yRange.max,
|
|
1350
|
-
if (
|
|
1351
|
-
const
|
|
1352
|
-
this.#
|
|
1404
|
+
const r = s.yRange.min, n = s.yRange.max, a = r !== this.#U || n !== this.#Z;
|
|
1405
|
+
if (a) {
|
|
1406
|
+
const d = this.#h.min !== void 0 && this.#h.min !== "auto", c = this.#h.max !== void 0 && this.#h.max !== "auto", f = i.media.height - this.xAxisHeight;
|
|
1407
|
+
this.#ht(r, n, f, d, c), this.#U = r, this.#Z = n;
|
|
1353
1408
|
}
|
|
1354
|
-
if (this.syncScales(), this.#s !== null && !this.#
|
|
1355
|
-
const
|
|
1356
|
-
if (
|
|
1357
|
-
const f = this.#n.size.media.width - this.yAxisWidth,
|
|
1358
|
-
Math.abs(
|
|
1409
|
+
if (this.syncScales(), this.#yt(), this.#s !== null && !this.#y) {
|
|
1410
|
+
const d = this.#e.lastXTarget ?? this.#o, c = d.to - d.from;
|
|
1411
|
+
if (c > 0) {
|
|
1412
|
+
const f = this.#n.size.media.width - this.yAxisWidth, p = C(this.#c.right, c, this.#a, f), v = this.#a * 0.5;
|
|
1413
|
+
Math.abs(d.to - (this.#s + p)) < v && (this.#y = !0);
|
|
1359
1414
|
}
|
|
1360
1415
|
}
|
|
1361
|
-
|
|
1362
|
-
let
|
|
1363
|
-
this.#n.useMainLayer((
|
|
1364
|
-
const { context:
|
|
1365
|
-
|
|
1366
|
-
const
|
|
1367
|
-
|
|
1368
|
-
scope:
|
|
1416
|
+
a && this.emit("viewportChange");
|
|
1417
|
+
let h = !1, l = !1;
|
|
1418
|
+
this.#n.useMainLayer((d) => {
|
|
1419
|
+
const { context: c, bitmapSize: f } = d, p = (i.media.width - this.yAxisWidth) * i.horizontalPixelRatio, v = (i.media.height - this.xAxisHeight) * i.verticalPixelRatio;
|
|
1420
|
+
c.clearRect(0, 0, f.width, f.height), c.save(), c.beginPath(), c.rect(0, 0, p, v), c.clip(), this.yScale.tickTracker.setCurrentTicks(this.yScale.niceTickValues()), this.timeScale.tickTracker.setCurrentTicks(this.timeScale.niceTickValues(this.#a).ticks), this.yScale.tickTracker.tick(e), this.timeScale.tickTracker.tick(e);
|
|
1421
|
+
const b = this.yScale.tickTracker.snapshot(), x = this.timeScale.tickTracker.snapshot();
|
|
1422
|
+
h = b.isAnimating, l = x.isAnimating, (b.isAnimating || x.isAnimating || s.animating) && this.emit("tickFrame"), this.#E && K({
|
|
1423
|
+
scope: d,
|
|
1369
1424
|
timeScale: this.timeScale,
|
|
1370
1425
|
yScale: this.yScale,
|
|
1371
1426
|
theme: this.#r,
|
|
1372
|
-
yTicks:
|
|
1373
|
-
timeTicks:
|
|
1374
|
-
}), this.#
|
|
1375
|
-
const k = this.#
|
|
1376
|
-
for (const
|
|
1377
|
-
const
|
|
1378
|
-
if (
|
|
1427
|
+
yTicks: b,
|
|
1428
|
+
timeTicks: x
|
|
1429
|
+
}), this.#ut(d, p, v);
|
|
1430
|
+
const k = this.#c, P = { top: k.top, bottom: k.bottom }, w = this.#S;
|
|
1431
|
+
for (const y of this.#t) {
|
|
1432
|
+
const S = y.renderer.getAlpha?.() ?? (y.visible ? 1 : 0);
|
|
1433
|
+
if (S <= 0) continue;
|
|
1379
1434
|
const L = {
|
|
1380
|
-
scope:
|
|
1435
|
+
scope: d,
|
|
1381
1436
|
timeScale: this.timeScale,
|
|
1382
1437
|
yScale: this.yScale,
|
|
1383
1438
|
theme: this.#r,
|
|
1384
|
-
dataInterval: this.#
|
|
1439
|
+
dataInterval: this.#a,
|
|
1385
1440
|
padding: P,
|
|
1386
1441
|
state: s,
|
|
1387
|
-
seriesId:
|
|
1388
|
-
}, A =
|
|
1389
|
-
if (
|
|
1442
|
+
seriesId: y.id
|
|
1443
|
+
}, A = c.globalAlpha;
|
|
1444
|
+
if (S < 1 && (c.globalAlpha = A * S), w) {
|
|
1390
1445
|
const V = performance.now();
|
|
1391
|
-
|
|
1446
|
+
y.renderer.render(L), w.recordSeries(y.id, performance.now() - V, e);
|
|
1392
1447
|
} else
|
|
1393
|
-
|
|
1394
|
-
|
|
1448
|
+
y.renderer.render(L);
|
|
1449
|
+
S < 1 && (c.globalAlpha = A);
|
|
1395
1450
|
}
|
|
1396
|
-
|
|
1451
|
+
c.restore();
|
|
1397
1452
|
});
|
|
1398
|
-
let
|
|
1399
|
-
for (const
|
|
1400
|
-
if (
|
|
1401
|
-
|
|
1453
|
+
let o = !1;
|
|
1454
|
+
for (const d of this.#t)
|
|
1455
|
+
if (d.renderer.needsAnimation) {
|
|
1456
|
+
o = !0, this.#i.markDirty();
|
|
1402
1457
|
break;
|
|
1403
1458
|
}
|
|
1404
|
-
const u =
|
|
1405
|
-
!s.animating && !
|
|
1459
|
+
const u = h || l;
|
|
1460
|
+
!s.animating && !o && !u && (this.yScale.tickTracker.markArmed(), this.timeScale.tickTracker.markArmed());
|
|
1406
1461
|
}
|
|
1407
1462
|
/**
|
|
1408
1463
|
* Paint a full frame: the main layer, then the overlay synchronously on
|
|
@@ -1413,80 +1468,80 @@ class Ot extends st {
|
|
|
1413
1468
|
* `markDirty` calls made *during* `renderOverlay` (pulse, edge spinner)
|
|
1414
1469
|
* land after the consume and re-arm cleanly for the next frame.
|
|
1415
1470
|
*/
|
|
1416
|
-
#
|
|
1417
|
-
this.#
|
|
1471
|
+
#G(t) {
|
|
1472
|
+
this.#j("main", t, () => this.renderMain(t)), this.#l.consume(), this.#j("overlay", t, () => this.renderOverlay(t));
|
|
1418
1473
|
}
|
|
1419
1474
|
/** Run a layer's paint, booking duration + draw calls to the perf monitor
|
|
1420
1475
|
* when one is attached — keeps overlay time out of the `main` samples. */
|
|
1421
|
-
#
|
|
1422
|
-
const s = this.#
|
|
1476
|
+
#j(t, i, e) {
|
|
1477
|
+
const s = this.#S;
|
|
1423
1478
|
if (!s) {
|
|
1424
1479
|
e();
|
|
1425
1480
|
return;
|
|
1426
1481
|
}
|
|
1427
|
-
s.resetDrawCalls(
|
|
1482
|
+
s.resetDrawCalls(t);
|
|
1428
1483
|
const r = performance.now();
|
|
1429
|
-
e(), s.recordFrame(
|
|
1484
|
+
e(), s.recordFrame(t, performance.now() - r, i ?? r);
|
|
1430
1485
|
}
|
|
1431
1486
|
/** Cheap overlay: crosshair, nearest-point dots, pulse animation, edge indicator. */
|
|
1432
|
-
renderOverlay(
|
|
1433
|
-
const
|
|
1434
|
-
if (
|
|
1487
|
+
renderOverlay(t) {
|
|
1488
|
+
const i = this.#n.size;
|
|
1489
|
+
if (i.media.width === 0 || i.media.height === 0) return;
|
|
1435
1490
|
let e = !1;
|
|
1436
|
-
for (const
|
|
1437
|
-
if (
|
|
1491
|
+
for (const a of this.#t)
|
|
1492
|
+
if (a.visible && a.renderer.overlayNeedsAnimation) {
|
|
1438
1493
|
e = !0;
|
|
1439
1494
|
break;
|
|
1440
1495
|
}
|
|
1441
|
-
this.#
|
|
1442
|
-
const s = this.#
|
|
1443
|
-
if (!(!n && !this.#
|
|
1444
|
-
if (this.#
|
|
1496
|
+
this.#m.some((a) => a.pulse) && (e = !0);
|
|
1497
|
+
const s = this.#x.left === "loading" || this.#x.right === "loading", r = this.#x.left !== "idle" || this.#x.right !== "idle", n = !!this.#d || e || r || this.#m.length > 0 || this.#g.length > 0;
|
|
1498
|
+
if (!(!n && !this.#q)) {
|
|
1499
|
+
if (this.#q = n, this.#n.useOverlayLayer((a) => {
|
|
1445
1500
|
if (!n) return;
|
|
1446
|
-
const
|
|
1447
|
-
if (
|
|
1448
|
-
const
|
|
1449
|
-
J(
|
|
1501
|
+
const h = (i.media.width - this.yAxisWidth) * i.horizontalPixelRatio, l = (i.media.height - this.xAxisHeight) * i.verticalPixelRatio;
|
|
1502
|
+
if (a.context.save(), a.context.beginPath(), a.context.rect(0, 0, h, l), a.context.clip(), this.#d && this.#gt()) {
|
|
1503
|
+
const c = this.#d.mediaX * i.horizontalPixelRatio, f = this.#d.mediaY * i.verticalPixelRatio;
|
|
1504
|
+
J(a, c, f, this.#r);
|
|
1450
1505
|
}
|
|
1451
|
-
const
|
|
1452
|
-
for (const
|
|
1453
|
-
|
|
1454
|
-
scope:
|
|
1506
|
+
const o = this.#c, u = { top: o.top, bottom: o.bottom }, d = this.#e.getAnimationState();
|
|
1507
|
+
for (const c of this.#t)
|
|
1508
|
+
c.visible && c.renderer.drawOverlay?.({
|
|
1509
|
+
scope: a,
|
|
1455
1510
|
timeScale: this.timeScale,
|
|
1456
1511
|
yScale: this.yScale,
|
|
1457
1512
|
theme: this.#r,
|
|
1458
|
-
dataInterval: this.#
|
|
1513
|
+
dataInterval: this.#a,
|
|
1459
1514
|
padding: u,
|
|
1460
|
-
crosshair: this.#
|
|
1461
|
-
state:
|
|
1462
|
-
seriesId:
|
|
1515
|
+
crosshair: this.#d,
|
|
1516
|
+
state: d,
|
|
1517
|
+
seriesId: c.id
|
|
1463
1518
|
});
|
|
1464
|
-
this.#
|
|
1465
|
-
scope:
|
|
1466
|
-
chartMediaHeight:
|
|
1519
|
+
this.#ft(a, h, l), this.#mt(a), r && E({
|
|
1520
|
+
scope: a,
|
|
1521
|
+
chartMediaHeight: i.media.height - this.xAxisHeight,
|
|
1467
1522
|
timeScale: this.timeScale,
|
|
1468
1523
|
theme: this.#r,
|
|
1469
|
-
edgeStates: this.#
|
|
1470
|
-
resolveBoundary: (
|
|
1471
|
-
}),
|
|
1524
|
+
edgeStates: this.#x,
|
|
1525
|
+
resolveBoundary: (c) => F(c, this.#J[c], this.getDataBounds())
|
|
1526
|
+
}), a.context.restore();
|
|
1472
1527
|
}), e) {
|
|
1473
|
-
const { from:
|
|
1474
|
-
let
|
|
1475
|
-
for (const
|
|
1476
|
-
if (!(!
|
|
1477
|
-
|
|
1528
|
+
const { from: a, to: h } = this.timeScale.getRange();
|
|
1529
|
+
let l = !1;
|
|
1530
|
+
for (const o of this.#t)
|
|
1531
|
+
if (!(!o.visible || !o.renderer.overlayNeedsAnimation) && (o.renderer.hasOverlayContentInRange?.(a, h) ?? !0)) {
|
|
1532
|
+
l = !0;
|
|
1478
1533
|
break;
|
|
1479
1534
|
}
|
|
1480
|
-
if (!
|
|
1481
|
-
for (const
|
|
1482
|
-
if (
|
|
1483
|
-
|
|
1535
|
+
if (!l) {
|
|
1536
|
+
for (const o of this.#m)
|
|
1537
|
+
if (o.pulse && o.time >= a && o.time <= h) {
|
|
1538
|
+
l = !0;
|
|
1484
1539
|
break;
|
|
1485
1540
|
}
|
|
1486
1541
|
}
|
|
1487
|
-
|
|
1542
|
+
l && this.#l.markDirty();
|
|
1488
1543
|
}
|
|
1489
|
-
s && this.#
|
|
1544
|
+
s && this.#l.markDirty();
|
|
1490
1545
|
}
|
|
1491
1546
|
}
|
|
1492
1547
|
}
|