@wick-charts/vue 0.6.0 → 0.6.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/README.md +27 -3
- package/dist/ChartContainer.vue.js +99 -69
- package/dist/core/dist/chart/options.js +27 -16
- package/dist/core/dist/chart.js +566 -366
- package/dist/core/dist/components/crosshair.js +8 -6
- package/dist/core/dist/interactions/handler.js +36 -20
- package/dist/index.d.ts +90 -0
- package/package.json +2 -2
- package/src/ChartContainer.vue +116 -1
package/dist/core/dist/chart.js
CHANGED
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
import { AnimationConfig as
|
|
2
|
-
import { easeOutCubic as
|
|
3
|
-
import { createViewportEngine as
|
|
4
|
-
import { CanvasManager as
|
|
5
|
-
import { resolveEdgeAnchorValue as
|
|
6
|
-
import { computeFitToData as
|
|
7
|
-
import { getLastValue as
|
|
8
|
-
import { resolveMaxVisibleBars as
|
|
9
|
-
import { computePan as
|
|
10
|
-
import { StreamingCadence as
|
|
11
|
-
import { computeStreamingTarget as
|
|
12
|
-
import { resolvePaddingTime as
|
|
13
|
-
import { computeTargetYRange as
|
|
14
|
-
import { renderCrosshair as
|
|
15
|
-
import { renderGrid as
|
|
16
|
-
import { renderMarker as
|
|
17
|
-
import { renderReferenceLine as
|
|
18
|
-
import { resolveTimeRegionColors as
|
|
19
|
-
import { EventEmitter as
|
|
20
|
-
import { InteractionHandler as
|
|
21
|
-
import { RenderScheduler as
|
|
22
|
-
import { XScale as
|
|
23
|
-
import { YScale as
|
|
24
|
-
import { resolveSeriesDefinition as
|
|
25
|
-
import { isTimeSeriesRenderer as
|
|
26
|
-
import { catppuccin as
|
|
27
|
-
import { clamp as
|
|
28
|
-
import { normalizeTime as
|
|
29
|
-
const
|
|
30
|
-
let
|
|
31
|
-
function
|
|
32
|
-
return
|
|
1
|
+
import { AnimationConfig as O, DEFAULT_LINE_PULSE as N } from "./animation/config.js";
|
|
2
|
+
import { easeOutCubic as R } from "./animation/easing.js";
|
|
3
|
+
import { createViewportEngine as E } from "./animation/viewport-engine.js";
|
|
4
|
+
import { CanvasManager as Y } from "./canvas-manager.js";
|
|
5
|
+
import { resolveEdgeAnchorValue as X, drawEdgeIndicators as Z, resolveEdgeBoundary as $ } from "./chart/edge-indicators.js";
|
|
6
|
+
import { computeFitToData as _ } from "./chart/fit-to-data.js";
|
|
7
|
+
import { getLastValue as G, getPreviousClose as U, getStackedLastValue as K } from "./chart/last-value.js";
|
|
8
|
+
import { resolveFade as C, resolveMaxVisibleBars as j, resolvePadding as I, resolvePerfOptions as q, isSameHorizontalPadding as P } from "./chart/options.js";
|
|
9
|
+
import { computePan as J, computeZoom as Q } from "./chart/pan-zoom-math.js";
|
|
10
|
+
import { StreamingCadence as tt } from "./chart/streaming-cadence.js";
|
|
11
|
+
import { computeStreamingTarget as it } from "./chart/streaming-target.js";
|
|
12
|
+
import { resolvePaddingTime as H } from "./chart/viewport-padding.js";
|
|
13
|
+
import { computeTargetYRange as et, resolveBound as z } from "./chart/y-target.js";
|
|
14
|
+
import { renderCrosshair as st } from "./components/crosshair.js";
|
|
15
|
+
import { renderGrid as rt } from "./components/grid.js";
|
|
16
|
+
import { renderMarker as nt } from "./components/marker.js";
|
|
17
|
+
import { renderReferenceLine as at } from "./components/reference-line.js";
|
|
18
|
+
import { resolveTimeRegionColors as ht, renderTimeRegion as ot } from "./components/time-region.js";
|
|
19
|
+
import { EventEmitter as lt } from "./events.js";
|
|
20
|
+
import { InteractionHandler as ct } from "./interactions/handler.js";
|
|
21
|
+
import { RenderScheduler as V } from "./render-scheduler.js";
|
|
22
|
+
import { XScale as dt } from "./scales/x-scale.js";
|
|
23
|
+
import { YScale as mt } from "./scales/y-scale.js";
|
|
24
|
+
import { resolveSeriesDefinition as ut } from "./series/definition.js";
|
|
25
|
+
import { isTimeSeriesRenderer as x } from "./series/types.js";
|
|
26
|
+
import { catppuccin as ft } from "./theme/themes/catppuccin.js";
|
|
27
|
+
import { clamp as gt } from "./utils/math.js";
|
|
28
|
+
import { normalizeTime as v, detectInterval as pt } from "./utils/time.js";
|
|
29
|
+
const yt = 200, vt = 1500, bt = 48, xt = 12, A = 9;
|
|
30
|
+
let St = 0, kt = 0, wt = 0, At = 0;
|
|
31
|
+
function W(g, t) {
|
|
32
|
+
return g === t ? !0 : !g || !t || g.length !== t.length ? !1 : g.every((i, e) => i === t[e]);
|
|
33
33
|
}
|
|
34
|
-
function
|
|
35
|
-
return
|
|
34
|
+
function Mt(g, t) {
|
|
35
|
+
return g === t ? !0 : !g || !t || g.length !== t.length ? !1 : g.every((i, e) => i === t[e]);
|
|
36
36
|
}
|
|
37
|
-
class
|
|
37
|
+
class ti extends lt {
|
|
38
38
|
/** Canvas lifecycle and DPR-aware sizing. */
|
|
39
39
|
#a;
|
|
40
40
|
/** Schedules main-layer redraws (background, grid, series). */
|
|
@@ -46,7 +46,7 @@ class qt extends at {
|
|
|
46
46
|
/** Maps price/value to vertical pixel coordinates. */
|
|
47
47
|
yScale;
|
|
48
48
|
/** Zoom, pan, crosshair — null when interactive=false. */
|
|
49
|
-
#
|
|
49
|
+
#T;
|
|
50
50
|
/** All registered series (candlestick, line, bar, pie). */
|
|
51
51
|
#t = [];
|
|
52
52
|
/** Point annotations (event markers) drawn on the overlay layer, kept out of the series model. */
|
|
@@ -63,12 +63,18 @@ class qt extends at {
|
|
|
63
63
|
* later — which re-arms `getIntroFront()`'s global min — can't blink settled
|
|
64
64
|
* markers / reference lines back out.
|
|
65
65
|
*/
|
|
66
|
-
#
|
|
67
|
-
#
|
|
66
|
+
#rt = !1;
|
|
67
|
+
#nt = !1;
|
|
68
68
|
/** Active visual theme (colors, fonts, grid style). */
|
|
69
69
|
#n;
|
|
70
70
|
/** Whether to render the background grid. */
|
|
71
|
-
#
|
|
71
|
+
#V;
|
|
72
|
+
/** Resolved edge-fade zones — `right: null` tracks the live Y-axis width. */
|
|
73
|
+
#k;
|
|
74
|
+
/** Memoized destination-out mask gradients, one per edge, keyed by the
|
|
75
|
+
* zone's bitmap geometry so a resize / DPR change / `setFade` rebuilds
|
|
76
|
+
* them and steady frames reuse them. */
|
|
77
|
+
#w = { top: null, right: null, left: null, tail: null };
|
|
72
78
|
/** Detected time interval between data points (milliseconds). */
|
|
73
79
|
#r = 6e4;
|
|
74
80
|
/** Current crosshair position, null when cursor is outside the chart. */
|
|
@@ -80,45 +86,45 @@ class qt extends at {
|
|
|
80
86
|
* `time`/`y`; `'time'` (programmatic {@link setCrosshair}) keeps
|
|
81
87
|
* `time`/`y` and re-projects the pixel position.
|
|
82
88
|
*/
|
|
83
|
-
#
|
|
89
|
+
#X = "pointer";
|
|
84
90
|
/** Whether the last overlay pass painted anything — gates the full-bitmap
|
|
85
91
|
* clear so an empty overlay isn't cleared again every main frame. */
|
|
86
|
-
#
|
|
92
|
+
#at = !1;
|
|
87
93
|
/** User-specified Y-axis bounds (auto, fixed, percentage). */
|
|
88
94
|
#l = {};
|
|
89
95
|
/** Cached series ID list — invalidated on add/remove. */
|
|
90
|
-
#
|
|
96
|
+
#D = null;
|
|
91
97
|
/** Axis visibility and sizing configuration. */
|
|
92
|
-
#
|
|
98
|
+
#W = {};
|
|
93
99
|
/** Host-declared state per edge — drives the edge-indicator overlay. */
|
|
94
|
-
#
|
|
100
|
+
#x = { left: "idle", right: "idle" };
|
|
95
101
|
/** Wall-clock start of a `loading` exit fade per side — set when the host
|
|
96
102
|
* drops `loading` back to `idle`/`has-more`, so the indicator fades out
|
|
97
103
|
* over {@link EDGE_EXIT_FADE_MS} instead of vanishing the same frame the
|
|
98
104
|
* fetched data lands. `null` = no fade in flight. */
|
|
99
|
-
#
|
|
105
|
+
#B = { left: null, right: null };
|
|
100
106
|
/** Cached boundary timestamps from the last `edgeReached` emission, by side. */
|
|
101
|
-
#
|
|
107
|
+
#ht = { left: null, right: null };
|
|
102
108
|
/** Latest placeholder shapes a `loading` indicator reported per side —
|
|
103
109
|
* the morph hand-off candidate consumed by {@link prependData}. */
|
|
104
|
-
#
|
|
110
|
+
#Z = { left: null, right: null };
|
|
105
111
|
/** True after a prepend consumed a side's placeholder snapshot: the series'
|
|
106
112
|
* reveal owns those pixels now, so the still-`loading` indicator is hidden
|
|
107
113
|
* and the upcoming `loading → idle` transition skips its exit fade. Reset
|
|
108
114
|
* by the next {@link setEdgeState} call. */
|
|
109
|
-
#
|
|
115
|
+
#I = { left: !1, right: !1 };
|
|
110
116
|
/** Host-supplied callback fired when the user releases a pan/zoom past a data edge. */
|
|
111
|
-
#
|
|
117
|
+
#ot;
|
|
112
118
|
/** Per-side custom painter for the edge `loading` spinner. `null` falls back to the built-in dots. */
|
|
113
|
-
#
|
|
119
|
+
#lt = { left: null, right: null };
|
|
114
120
|
/** Nesting depth for batch updates. Suppresses recomputes while > 0. */
|
|
115
|
-
#
|
|
121
|
+
#M = 0;
|
|
116
122
|
/** True when batched operations include data changes (triggers full onDataChanged on end). */
|
|
117
|
-
|
|
123
|
+
#$ = !1;
|
|
118
124
|
/** True when batched operations include visibility changes (triggers Y-range + redraw on end). */
|
|
119
|
-
#
|
|
125
|
+
#P = !1;
|
|
120
126
|
/** True when batched operations bumped overlay version (emits a single overlayChange on flush). */
|
|
121
|
-
#
|
|
127
|
+
#_ = !1;
|
|
122
128
|
/**
|
|
123
129
|
* Monotonic counter bumped on any mutation that affects overlay output —
|
|
124
130
|
* data, visibility, series options, theme. Used by snapshot helpers in
|
|
@@ -128,13 +134,13 @@ class qt extends at {
|
|
|
128
134
|
*
|
|
129
135
|
* @internal
|
|
130
136
|
*/
|
|
131
|
-
#
|
|
137
|
+
#ct = 0;
|
|
132
138
|
get yAxisWidth() {
|
|
133
|
-
const t = this.#
|
|
139
|
+
const t = this.#W.y;
|
|
134
140
|
return t?.visible === !1 ? 0 : t?.width ?? 55;
|
|
135
141
|
}
|
|
136
142
|
get xAxisHeight() {
|
|
137
|
-
const t = this.#
|
|
143
|
+
const t = this.#W.x;
|
|
138
144
|
return t?.visible === !1 ? 0 : t?.height ?? 30;
|
|
139
145
|
}
|
|
140
146
|
/** Resolved animation config derived from `options.animations` at construction. */
|
|
@@ -153,7 +159,7 @@ class qt extends at {
|
|
|
153
159
|
* longer than the producer's tick interval — the spring never quite
|
|
154
160
|
* settles between ticks, so velocity carries continuously and the slide
|
|
155
161
|
* doesn't pulse. */
|
|
156
|
-
#
|
|
162
|
+
#R;
|
|
157
163
|
/**
|
|
158
164
|
* Mirrors `#dataReplaceSnapPending` for the engine signal that follows
|
|
159
165
|
* inside the same `onDataChanged` call. `#dataReplaceSnapPending` is
|
|
@@ -163,9 +169,9 @@ class qt extends at {
|
|
|
163
169
|
* field carries the decision across that gap. The closure also reads
|
|
164
170
|
* `#dataStart` / `#dataEnd` directly for the timestamps.
|
|
165
171
|
*/
|
|
166
|
-
#
|
|
172
|
+
#dt = !1;
|
|
167
173
|
/** Earliest data timestamp registered across all series, `null` before any data has arrived. */
|
|
168
|
-
#
|
|
174
|
+
#A = null;
|
|
169
175
|
/** Latest data timestamp registered across all series, `null` before any data has arrived. */
|
|
170
176
|
#i = null;
|
|
171
177
|
/**
|
|
@@ -174,20 +180,20 @@ class qt extends at {
|
|
|
174
180
|
* left of the tail keeps that offset as new bars arrive, instead of
|
|
175
181
|
* snapping back to the natural-pin position every frame.
|
|
176
182
|
*/
|
|
177
|
-
#
|
|
183
|
+
#F = null;
|
|
178
184
|
/**
|
|
179
185
|
* Warm-up window flag — `setVisibleRange({ from, bars })` arms it and
|
|
180
186
|
* the streaming-target math suppresses pan ticks while the data hasn't
|
|
181
187
|
* reached the right edge yet. Cleared by user interaction or once the
|
|
182
188
|
* data fills the window.
|
|
183
189
|
*/
|
|
184
|
-
#
|
|
190
|
+
#L = !1;
|
|
185
191
|
/**
|
|
186
192
|
* Cap for fit-to-data. When the data span exceeds this, the fit anchors
|
|
187
193
|
* the right edge and trims the left. Resolved once at construction from
|
|
188
194
|
* `options.viewport.maxVisibleBars`.
|
|
189
195
|
*/
|
|
190
|
-
#
|
|
196
|
+
#mt;
|
|
191
197
|
/**
|
|
192
198
|
* Committed logical X range — the "where X should be" target. Engine
|
|
193
199
|
* eases its visual toward this. Pan / zoom / setRange / fitContent /
|
|
@@ -196,7 +202,7 @@ class qt extends at {
|
|
|
196
202
|
*/
|
|
197
203
|
#o = { from: 0, to: 0 };
|
|
198
204
|
/** Padded Y range — engine's raw Y target plus pixel padding from `#padding`. */
|
|
199
|
-
#
|
|
205
|
+
#O = { min: 0, max: 0 };
|
|
200
206
|
/**
|
|
201
207
|
* Auto-scroll flag — when `true`, streaming ticks slide X to track the
|
|
202
208
|
* data tail. Pan flips this off when the gesture pushes the tail off
|
|
@@ -208,17 +214,17 @@ class qt extends at {
|
|
|
208
214
|
#c;
|
|
209
215
|
/** Cached chart width — passed by interactions on every call; kept for the rare
|
|
210
216
|
* call that omits it (e.g. programmatic `chart.pan(...)` without explicit width). */
|
|
211
|
-
#
|
|
217
|
+
#N = 0;
|
|
212
218
|
/** Active performance monitor, or `null` when instrumentation is disabled (the default). */
|
|
213
|
-
#
|
|
219
|
+
#S;
|
|
214
220
|
/** When true, `destroy()` tears down the monitor; false for caller-supplied monitors we must not destroy. */
|
|
215
|
-
#
|
|
221
|
+
#G = !1;
|
|
216
222
|
/** Visible HUD overlay (mounted by the perf config); non-null only when the caller requested one. */
|
|
217
223
|
#U = null;
|
|
218
224
|
constructor(t, i) {
|
|
219
|
-
super(), i?.axis && (this.#
|
|
225
|
+
super(), i?.axis && (this.#W = i.axis, this.#l = { min: i.axis.y?.min, max: i.axis.y?.max }), this.#n = i?.theme ?? ft.theme, this.#V = i?.grid?.visible !== !1, this.#k = C(i?.fade), this.#g = O.resolve(i?.animations), this.#mt = j(i?.viewport?.maxVisibleBars), this.#c = I(i?.padding), this.#R = new tt();
|
|
220
226
|
const e = this.#g.axis.y, s = this.#g.axis.x;
|
|
221
|
-
this.#s =
|
|
227
|
+
this.#s = E({
|
|
222
228
|
initial: { yRange: { min: 0, max: 0 }, xRange: { from: 0, to: 0 } },
|
|
223
229
|
y: {
|
|
224
230
|
curve: e.curve,
|
|
@@ -233,25 +239,25 @@ class qt extends at {
|
|
|
233
239
|
settleMs: s.settleMs,
|
|
234
240
|
gestureMs: s.gestureMs
|
|
235
241
|
},
|
|
236
|
-
computeXTarget: () => this.#
|
|
237
|
-
computeYTarget: ({ xTarget:
|
|
242
|
+
computeXTarget: () => this.#Wt(),
|
|
243
|
+
computeYTarget: ({ xTarget: h }) => this.#xt(h),
|
|
238
244
|
onWake: () => this.#e?.markDirty()
|
|
239
|
-
}), this.#
|
|
240
|
-
const r =
|
|
241
|
-
this.#
|
|
245
|
+
}), this.#ot = i?.onEdgeReached, this.#E = i?.viewport?.initialRange;
|
|
246
|
+
const r = q(i?.perf);
|
|
247
|
+
this.#S = r.monitor, this.#G = r.ownsMonitor, this.#a = new Y(t, this.#S ?? void 0), this.timeScale = new dt(), this.yScale = new mt(), this.timeScale.setLocale(i?.locale), this.timeScale.setTimeZone(i?.timeZone);
|
|
242
248
|
const n = this.#g.axis.ticksMs;
|
|
243
|
-
this.timeScale.tickTracker.setFadeMs(n), this.yScale.tickTracker.setFadeMs(n), this.#e = new
|
|
244
|
-
(
|
|
245
|
-
), this.#
|
|
246
|
-
const
|
|
247
|
-
this.#
|
|
248
|
-
this.syncScales(), this.#
|
|
249
|
-
}), this.syncScales(), this.#
|
|
250
|
-
this.#
|
|
251
|
-
}), this.#
|
|
252
|
-
this.emit("pointClick", { ...
|
|
253
|
-
}), this.#
|
|
254
|
-
this.fitContent(), this.emit("pointDoubleClick", { ...
|
|
249
|
+
this.timeScale.tickTracker.setFadeMs(n), this.yScale.tickTracker.setFadeMs(n), this.#e = new V((h) => this.#et(h)), this.#h = new V(
|
|
250
|
+
(h) => this.#st("overlay", h, () => this.renderOverlay(h))
|
|
251
|
+
), this.#S && r.hud && (this.#U = r.hud(t, this.#S));
|
|
252
|
+
const a = i?.interactive !== !1;
|
|
253
|
+
this.#T = a ? new ct(this.#a.canvas, this, this.timeScale, this.yScale) : null, this.#a.on("resize", () => {
|
|
254
|
+
this.syncScales(), this.#et(), this.emit("viewportChange");
|
|
255
|
+
}), this.syncScales(), this.#T?.on("crosshairMove", (h) => {
|
|
256
|
+
this.#X = "pointer", this.#d = h, this.#h.markDirty(), this.emit("crosshairMove", h), this.#q(h);
|
|
257
|
+
}), this.#T?.on("click", (h) => {
|
|
258
|
+
this.emit("pointClick", { ...h, spatialHit: this.#bt(h) });
|
|
259
|
+
}), this.#T?.on("dblclick", (h) => {
|
|
260
|
+
this.fitContent(), this.emit("pointDoubleClick", { ...h, spatialHit: this.#bt(h) });
|
|
255
261
|
});
|
|
256
262
|
}
|
|
257
263
|
/**
|
|
@@ -267,8 +273,8 @@ class qt extends at {
|
|
|
267
273
|
* render instead of N.
|
|
268
274
|
*/
|
|
269
275
|
#p() {
|
|
270
|
-
if (this.#
|
|
271
|
-
this.#
|
|
276
|
+
if (this.#ct++, this.#M > 0) {
|
|
277
|
+
this.#_ = !0;
|
|
272
278
|
return;
|
|
273
279
|
}
|
|
274
280
|
this.emit("overlayChange");
|
|
@@ -282,39 +288,39 @@ class qt extends at {
|
|
|
282
288
|
* @internal
|
|
283
289
|
*/
|
|
284
290
|
getOverlayVersion() {
|
|
285
|
-
return this.#
|
|
291
|
+
return this.#ct;
|
|
286
292
|
}
|
|
287
293
|
/**
|
|
288
294
|
* Return a series ID: use the provided hint if it's non-empty and not already taken,
|
|
289
295
|
* otherwise generate a new auto ID. Auto-generated IDs never collide with each other
|
|
290
296
|
* or with user-provided IDs because they use a monotonically increasing counter.
|
|
291
297
|
*/
|
|
292
|
-
#
|
|
298
|
+
#Tt(t) {
|
|
293
299
|
if (t && !this.#t.some((e) => e.id === t))
|
|
294
300
|
return t;
|
|
295
301
|
let i;
|
|
296
302
|
do
|
|
297
|
-
i = `series_${++
|
|
303
|
+
i = `series_${++St}`;
|
|
298
304
|
while (this.#t.some((e) => e.id === i));
|
|
299
305
|
return i;
|
|
300
306
|
}
|
|
301
307
|
addSeries(t, i = {}) {
|
|
302
|
-
const e = typeof t == "string" ?
|
|
308
|
+
const e = typeof t == "string" ? ut(t) : t;
|
|
303
309
|
if (!e)
|
|
304
310
|
throw new Error(
|
|
305
311
|
`[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).`
|
|
306
312
|
);
|
|
307
|
-
const { layers: s, id: r, label: n, labels:
|
|
313
|
+
const { layers: s, id: r, label: n, labels: a, ...h } = i, l = s ?? 1, o = {
|
|
308
314
|
...this.#g.defaults(e.type),
|
|
309
|
-
...
|
|
315
|
+
...h,
|
|
310
316
|
...this.#g.overrides(e.type)
|
|
311
|
-
}, u = e.create({ theme: this.#n, layerCount: l }, o),
|
|
312
|
-
return u.onDataChanged?.(() => this.onDataChanged()), this.#t.push({ id:
|
|
317
|
+
}, u = e.create({ theme: this.#n, layerCount: l }, o), c = a ?? (typeof n == "string" ? [n] : void 0), d = this.#Tt(r);
|
|
318
|
+
return u.onDataChanged?.(() => this.onDataChanged()), this.#t.push({ id: d, labels: c, renderer: u, visible: !0 }), this.#D = null, this.#j(), this.emit("seriesChange"), this.#p(), d;
|
|
313
319
|
}
|
|
314
320
|
/** Remove a series by ID and clean up its resources. */
|
|
315
321
|
removeSeries(t) {
|
|
316
322
|
const i = this.#t.findIndex((e) => e.id === t);
|
|
317
|
-
i >= 0 && (this.#t[i].renderer.dispose(), this.#t.splice(i, 1), this.#
|
|
323
|
+
i >= 0 && (this.#t[i].renderer.dispose(), this.#t.splice(i, 1), this.#D = null, this.#j(), this.#e.markDirty(), this.emit("seriesChange"), this.#p());
|
|
318
324
|
}
|
|
319
325
|
// --- Markers (point annotations) -----------------------------------------
|
|
320
326
|
/**
|
|
@@ -327,13 +333,13 @@ class qt extends at {
|
|
|
327
333
|
* appear in series queries.
|
|
328
334
|
*/
|
|
329
335
|
addMarker(t) {
|
|
330
|
-
const i = `marker_${++
|
|
331
|
-
return this.#m.push(this.#
|
|
336
|
+
const i = `marker_${++kt}`;
|
|
337
|
+
return this.#m.push(this.#ut(i, t)), this.#h.markDirty(), i;
|
|
332
338
|
}
|
|
333
339
|
/** Replace a marker's config in place. No-op when the id is unknown. */
|
|
334
340
|
updateMarker(t, i) {
|
|
335
341
|
const e = this.#m.findIndex((s) => s.id === t);
|
|
336
|
-
e !== -1 && (this.#m[e] = this.#
|
|
342
|
+
e !== -1 && (this.#m[e] = this.#ut(t, i), this.#h.markDirty());
|
|
337
343
|
}
|
|
338
344
|
/** Remove a marker by id. No-op when the id is unknown. */
|
|
339
345
|
removeMarker(t) {
|
|
@@ -344,10 +350,10 @@ class qt extends at {
|
|
|
344
350
|
getMarkers() {
|
|
345
351
|
return this.#m;
|
|
346
352
|
}
|
|
347
|
-
#
|
|
353
|
+
#ut(t, i) {
|
|
348
354
|
return {
|
|
349
355
|
id: t,
|
|
350
|
-
time:
|
|
356
|
+
time: v(i.time),
|
|
351
357
|
value: i.value,
|
|
352
358
|
seriesId: i.seriesId,
|
|
353
359
|
shape: i.shape ?? "dot",
|
|
@@ -357,14 +363,14 @@ class qt extends at {
|
|
|
357
363
|
};
|
|
358
364
|
}
|
|
359
365
|
/** Resolve a marker's Y value: explicit `value`, else snapped to its series at `time`. Null when neither resolves. */
|
|
360
|
-
#
|
|
366
|
+
#Dt(t) {
|
|
361
367
|
if (t.value !== void 0 && Number.isFinite(t.value)) return t.value;
|
|
362
368
|
if (t.seriesId === void 0) return null;
|
|
363
369
|
const i = this.getDataAtTime(t.seriesId, t.time);
|
|
364
370
|
return i ? "close" in i ? i.close : i.value : null;
|
|
365
371
|
}
|
|
366
372
|
/** Resolve a marker's color: explicit `color`, else the series color, else the theme line color. */
|
|
367
|
-
#
|
|
373
|
+
#Rt(t) {
|
|
368
374
|
if (t.color !== void 0) return t.color;
|
|
369
375
|
if (t.seriesId !== void 0) {
|
|
370
376
|
const i = this.getSeriesColor(t.seriesId);
|
|
@@ -372,24 +378,24 @@ class qt extends at {
|
|
|
372
378
|
}
|
|
373
379
|
return this.#n.line.color;
|
|
374
380
|
}
|
|
375
|
-
#
|
|
381
|
+
#Ct(t, i, e) {
|
|
376
382
|
if (this.#m.length === 0) return;
|
|
377
|
-
const s = performance.now() / (
|
|
383
|
+
const s = performance.now() / (N * 2 * Math.PI) % 1, r = this.#K();
|
|
378
384
|
for (const n of this.#m) {
|
|
379
|
-
const
|
|
380
|
-
if (
|
|
381
|
-
const
|
|
382
|
-
if (
|
|
385
|
+
const a = this.#Dt(n);
|
|
386
|
+
if (a === null) continue;
|
|
387
|
+
const h = this.#pt(r, n.time);
|
|
388
|
+
if (h <= 0) continue;
|
|
383
389
|
const l = t.context.globalAlpha;
|
|
384
|
-
|
|
390
|
+
h < 1 && (t.context.globalAlpha = l * h), nt({
|
|
385
391
|
scope: t,
|
|
386
392
|
timeScale: this.timeScale,
|
|
387
393
|
yScale: this.yScale,
|
|
388
394
|
theme: this.#n,
|
|
389
395
|
marker: {
|
|
390
396
|
time: n.time,
|
|
391
|
-
value:
|
|
392
|
-
color: this.#
|
|
397
|
+
value: a,
|
|
398
|
+
color: this.#Rt(n),
|
|
393
399
|
shape: n.shape,
|
|
394
400
|
pulse: n.pulse,
|
|
395
401
|
label: n.label
|
|
@@ -397,7 +403,7 @@ class qt extends at {
|
|
|
397
403
|
phase: s,
|
|
398
404
|
plotWidth: i,
|
|
399
405
|
plotHeight: e
|
|
400
|
-
}),
|
|
406
|
+
}), h < 1 && (t.context.globalAlpha = l);
|
|
401
407
|
}
|
|
402
408
|
}
|
|
403
409
|
// --- Time regions (shaded bands) -----------------------------------------
|
|
@@ -413,13 +419,13 @@ class qt extends at {
|
|
|
413
419
|
* legend.
|
|
414
420
|
*/
|
|
415
421
|
addRegion(t) {
|
|
416
|
-
const i = `region_${++
|
|
417
|
-
return this.#b.push(this.#
|
|
422
|
+
const i = `region_${++wt}`;
|
|
423
|
+
return this.#b.push(this.#ft(i, t)), this.#e.markDirty(), i;
|
|
418
424
|
}
|
|
419
425
|
/** Replace a region's config in place. No-op when the id is unknown. */
|
|
420
426
|
updateRegion(t, i) {
|
|
421
427
|
const e = this.#b.findIndex((s) => s.id === t);
|
|
422
|
-
e !== -1 && (this.#b[e] = this.#
|
|
428
|
+
e !== -1 && (this.#b[e] = this.#ft(t, i), this.#e.markDirty());
|
|
423
429
|
}
|
|
424
430
|
/** Remove a region by id. No-op when the id is unknown. */
|
|
425
431
|
removeRegion(t) {
|
|
@@ -430,22 +436,22 @@ class qt extends at {
|
|
|
430
436
|
getRegions() {
|
|
431
437
|
return this.#b;
|
|
432
438
|
}
|
|
433
|
-
#
|
|
434
|
-
const { to: e } = i, s = e === void 0 || e === "now" ? null :
|
|
439
|
+
#ft(t, i) {
|
|
440
|
+
const { to: e } = i, s = e === void 0 || e === "now" ? null : v(e);
|
|
435
441
|
return {
|
|
436
442
|
id: t,
|
|
437
|
-
from:
|
|
443
|
+
from: v(i.from),
|
|
438
444
|
to: s,
|
|
439
445
|
fill: i.fill,
|
|
440
446
|
color: i.color,
|
|
441
447
|
label: i.label
|
|
442
448
|
};
|
|
443
449
|
}
|
|
444
|
-
#
|
|
450
|
+
#It(t, i, e) {
|
|
445
451
|
if (this.#b.length !== 0)
|
|
446
452
|
for (const s of this.#b) {
|
|
447
|
-
const { color: r, fill: n } =
|
|
448
|
-
|
|
453
|
+
const { color: r, fill: n } = ht(s, this.#n);
|
|
454
|
+
ot({
|
|
449
455
|
scope: t,
|
|
450
456
|
timeScale: this.timeScale,
|
|
451
457
|
theme: this.#n,
|
|
@@ -467,13 +473,13 @@ class qt extends at {
|
|
|
467
473
|
* legend.
|
|
468
474
|
*/
|
|
469
475
|
addLine(t) {
|
|
470
|
-
const i = `line_${++
|
|
471
|
-
return this.#y.push(this.#
|
|
476
|
+
const i = `line_${++At}`;
|
|
477
|
+
return this.#y.push(this.#gt(i, t)), this.#h.markDirty(), i;
|
|
472
478
|
}
|
|
473
479
|
/** Replace a reference line's config in place. No-op when the id is unknown. */
|
|
474
480
|
updateLine(t, i) {
|
|
475
481
|
const e = this.#y.findIndex((s) => s.id === t);
|
|
476
|
-
e !== -1 && (this.#y[e] = this.#
|
|
482
|
+
e !== -1 && (this.#y[e] = this.#gt(t, i), this.#h.markDirty());
|
|
477
483
|
}
|
|
478
484
|
/** Remove a reference line by id. No-op when the id is unknown. */
|
|
479
485
|
removeLine(t) {
|
|
@@ -484,7 +490,7 @@ class qt extends at {
|
|
|
484
490
|
getLines() {
|
|
485
491
|
return this.#y;
|
|
486
492
|
}
|
|
487
|
-
#
|
|
493
|
+
#gt(t, i) {
|
|
488
494
|
const { time: e, value: s } = i, r = {
|
|
489
495
|
id: t,
|
|
490
496
|
color: i.color,
|
|
@@ -492,16 +498,16 @@ class qt extends at {
|
|
|
492
498
|
style: i.style ?? "dashed",
|
|
493
499
|
width: i.width ?? 1
|
|
494
500
|
};
|
|
495
|
-
return e !== void 0 ? { ...r, orientation: "vertical", coord:
|
|
501
|
+
return e !== void 0 ? { ...r, orientation: "vertical", coord: v(e) } : { ...r, orientation: "horizontal", coord: s ?? Number.NaN };
|
|
496
502
|
}
|
|
497
|
-
#
|
|
503
|
+
#Pt(t, i, e) {
|
|
498
504
|
if (this.#y.length === 0) return;
|
|
499
|
-
const s = this
|
|
505
|
+
const s = this.#K();
|
|
500
506
|
for (const r of this.#y) {
|
|
501
|
-
const n = s >= 1 ? 1 : r.orientation === "vertical" ? this.#
|
|
507
|
+
const n = s >= 1 ? 1 : r.orientation === "vertical" ? this.#pt(s, r.coord) : R(s);
|
|
502
508
|
if (n <= 0) continue;
|
|
503
|
-
const
|
|
504
|
-
n < 1 && (t.context.globalAlpha =
|
|
509
|
+
const a = t.context.globalAlpha;
|
|
510
|
+
n < 1 && (t.context.globalAlpha = a * n), at({
|
|
505
511
|
scope: t,
|
|
506
512
|
timeScale: this.timeScale,
|
|
507
513
|
yScale: this.yScale,
|
|
@@ -516,7 +522,7 @@ class qt extends at {
|
|
|
516
522
|
},
|
|
517
523
|
plotWidth: i,
|
|
518
524
|
plotHeight: e
|
|
519
|
-
}), n < 1 && (t.context.globalAlpha =
|
|
525
|
+
}), n < 1 && (t.context.globalAlpha = a);
|
|
520
526
|
}
|
|
521
527
|
}
|
|
522
528
|
/**
|
|
@@ -554,10 +560,10 @@ class qt extends at {
|
|
|
554
560
|
* has actually been observed in progress, so an annotation drawn on a chart
|
|
555
561
|
* that has no data yet still rides the reveal once data arrives.
|
|
556
562
|
*/
|
|
557
|
-
|
|
558
|
-
if (this.#
|
|
563
|
+
#K() {
|
|
564
|
+
if (this.#nt) return 1;
|
|
559
565
|
const t = this.getIntroFront();
|
|
560
|
-
return t < 1 ? (this.#
|
|
566
|
+
return t < 1 ? (this.#rt = !0, t) : (this.#rt && (this.#nt = !0), 1);
|
|
561
567
|
}
|
|
562
568
|
/**
|
|
563
569
|
* Entrance alpha for an overlay pinned at `time`, given the intro reveal
|
|
@@ -565,10 +571,10 @@ class qt extends at {
|
|
|
565
571
|
* then fades in over a short band — so annotations surface in step with the
|
|
566
572
|
* series drawing past them. `1` once the intro has settled.
|
|
567
573
|
*/
|
|
568
|
-
#
|
|
574
|
+
#pt(t, i) {
|
|
569
575
|
if (t >= 1) return 1;
|
|
570
576
|
const { from: e, to: s } = this.timeScale.getRange(), r = s - e, n = r > 0 ? (i - e) / r : 0;
|
|
571
|
-
return
|
|
577
|
+
return gt((t - n) / 0.12, 0, 1);
|
|
572
578
|
}
|
|
573
579
|
/**
|
|
574
580
|
* Replace all data for a series.
|
|
@@ -581,7 +587,7 @@ class qt extends at {
|
|
|
581
587
|
*/
|
|
582
588
|
setSeriesData(t, i, e) {
|
|
583
589
|
const s = this.#t.find((r) => r.id === t);
|
|
584
|
-
s && (this.#
|
|
590
|
+
s && (this.#J = !0, s.renderer.setData(i, e));
|
|
585
591
|
}
|
|
586
592
|
/** Append a new data point to the end of a series (real-time tick). */
|
|
587
593
|
appendData(t, i, e) {
|
|
@@ -604,8 +610,8 @@ class qt extends at {
|
|
|
604
610
|
prependData(t, i, e) {
|
|
605
611
|
const s = this.#t.find((n) => n.id === t);
|
|
606
612
|
if (s === void 0 || s.renderer.prependPoints === void 0) return;
|
|
607
|
-
const r = this.#
|
|
608
|
-
r !== null && s.renderer.setHistoryHandoff !== void 0 && performance.now() - r.at <
|
|
613
|
+
const r = this.#Z.left;
|
|
614
|
+
r !== null && s.renderer.setHistoryHandoff !== void 0 && performance.now() - r.at < vt && (s.renderer.setHistoryHandoff(r.bars), this.#I.left = !0, this.#B.left = null, this.#Z.left = null, this.#h.markDirty()), s.renderer.prependPoints(i, e);
|
|
609
615
|
}
|
|
610
616
|
/** Update the last data point of a series in place (e.g. live candle update). */
|
|
611
617
|
updateData(t, i, e) {
|
|
@@ -633,27 +639,27 @@ class qt extends at {
|
|
|
633
639
|
if (!e) return;
|
|
634
640
|
const s = e.labels, r = e.renderer.getLayerColors().slice(), n = this.#g.overrides(e.renderer.kind);
|
|
635
641
|
e.renderer.updateOptions({ ...i, ...n }), "label" in i && typeof i.label == "string" && (e.labels = [i.label]), this.#e.markDirty();
|
|
636
|
-
const
|
|
637
|
-
(
|
|
642
|
+
const a = e.renderer.getLayerColors(), h = r.length !== a.length || r.some((o, u) => o !== a[u]), l = !W(s, e.labels);
|
|
643
|
+
(h || l) && this.#p();
|
|
638
644
|
}
|
|
639
645
|
/**
|
|
640
646
|
* Batch multiple updates: suppress recomputes until `fn` returns. Exceptions
|
|
641
647
|
* inside `fn` still flush the batch so counters don't leak across calls.
|
|
642
648
|
*/
|
|
643
649
|
batch(t) {
|
|
644
|
-
this.#
|
|
650
|
+
this.#M++;
|
|
645
651
|
try {
|
|
646
652
|
t();
|
|
647
653
|
} finally {
|
|
648
|
-
if (--this.#
|
|
649
|
-
if (this.#
|
|
650
|
-
this
|
|
651
|
-
else if (this.#
|
|
652
|
-
this.#
|
|
654
|
+
if (--this.#M <= 0) {
|
|
655
|
+
if (this.#M = 0, this.#$)
|
|
656
|
+
this.#$ = !1, this.#P = !1, this.onDataChanged();
|
|
657
|
+
else if (this.#P) {
|
|
658
|
+
this.#P = !1, this.#f = !0;
|
|
653
659
|
const i = performance.now();
|
|
654
660
|
this.#s.onAxisReconfig(i), this.#v(i), this.#e.markDirty();
|
|
655
661
|
}
|
|
656
|
-
this.#
|
|
662
|
+
this.#_ && (this.#_ = !1, this.emit("overlayChange"));
|
|
657
663
|
}
|
|
658
664
|
}
|
|
659
665
|
}
|
|
@@ -667,10 +673,10 @@ class qt extends at {
|
|
|
667
673
|
setSeriesVisible(t, i) {
|
|
668
674
|
const e = this.#t.find((n) => n.id === t);
|
|
669
675
|
if (!e || e.visible === i) return;
|
|
670
|
-
e.visible = i, this.#p();
|
|
676
|
+
e.visible = i, this.#p(), this.#j();
|
|
671
677
|
const s = this.#g.toggleMs;
|
|
672
|
-
if (e.renderer.setAlpha?.(i ? 1 : 0, s), this.#e.markDirty(), this.#
|
|
673
|
-
this.#
|
|
678
|
+
if (e.renderer.setAlpha?.(i ? 1 : 0, s), this.#e.markDirty(), this.#M > 0) {
|
|
679
|
+
this.#P = !0;
|
|
674
680
|
return;
|
|
675
681
|
}
|
|
676
682
|
this.#f = !0;
|
|
@@ -701,12 +707,12 @@ class qt extends at {
|
|
|
701
707
|
* layer's geometry on screen until the engine's Y ease settles.
|
|
702
708
|
*/
|
|
703
709
|
setLayerVisible(t, i, e) {
|
|
704
|
-
const s = this.#t.find((
|
|
710
|
+
const s = this.#t.find((a) => a.id === t);
|
|
705
711
|
if (!s || s.renderer.getLayerCount() <= 1 || s.renderer.isLayerVisible(i) === e) return;
|
|
706
712
|
s.renderer.setLayerVisible(i, e), this.#p();
|
|
707
713
|
const r = this.#g.toggleMs;
|
|
708
|
-
if (s.renderer.setLayerAlpha?.(i, e ? 1 : 0, r), this.#e.markDirty(), this.#
|
|
709
|
-
this.#
|
|
714
|
+
if (s.renderer.setLayerAlpha?.(i, e ? 1 : 0, r), this.#e.markDirty(), this.#M > 0) {
|
|
715
|
+
this.#P = !0;
|
|
710
716
|
return;
|
|
711
717
|
}
|
|
712
718
|
this.#f = !0;
|
|
@@ -721,7 +727,7 @@ class qt extends at {
|
|
|
721
727
|
const { first: t, last: i } = this.getDataBounds();
|
|
722
728
|
if (t === void 0 || i === void 0) return;
|
|
723
729
|
const e = this.#a.size.media.width - this.yAxisWidth;
|
|
724
|
-
this.#
|
|
730
|
+
this.#z(t, i, e), this.#Y({ xEase: !0 });
|
|
725
731
|
}
|
|
726
732
|
getVisibleRange() {
|
|
727
733
|
return this.#o.from === 0 && this.#o.to === 0 ? this.#s.getAnimationState().xRange : this.#o;
|
|
@@ -732,7 +738,7 @@ class qt extends at {
|
|
|
732
738
|
* tracking and is cheaper than recomputing from series stores.
|
|
733
739
|
*/
|
|
734
740
|
getDataRange() {
|
|
735
|
-
return this.#
|
|
741
|
+
return this.#A === null || this.#i === null ? null : { from: this.#A, to: this.#i };
|
|
736
742
|
}
|
|
737
743
|
/**
|
|
738
744
|
* Imperatively set the visible time range. Three forms:
|
|
@@ -761,29 +767,29 @@ class qt extends at {
|
|
|
761
767
|
setVisibleRange(t, i) {
|
|
762
768
|
if (typeof t == "number") {
|
|
763
769
|
if (!Number.isInteger(t) || t < 2) return;
|
|
764
|
-
const { first: n, last:
|
|
765
|
-
if (n === void 0 ||
|
|
766
|
-
const
|
|
767
|
-
this.#
|
|
770
|
+
const { first: n, last: a } = this.getDataBounds();
|
|
771
|
+
if (n === void 0 || a === void 0) return;
|
|
772
|
+
const h = Math.max(n, a - (t - 1) * this.#r), l = this.#a.size.media.width - this.yAxisWidth;
|
|
773
|
+
this.#z(h, a, l), this.#Y();
|
|
768
774
|
return;
|
|
769
775
|
}
|
|
770
776
|
if ("bars" in t) {
|
|
771
777
|
if (!Number.isInteger(t.bars) || t.bars < 2) return;
|
|
772
|
-
const n =
|
|
773
|
-
this.#u = this.#i !== null && this.#i >= n && this.#i <=
|
|
778
|
+
const n = v(t.from), a = n + t.bars * this.#r;
|
|
779
|
+
this.#u = this.#i !== null && this.#i >= n && this.#i <= a, this.#C({ from: n, to: a }, { emitChange: !0 }), this.#L = !0, this.#Y();
|
|
774
780
|
return;
|
|
775
781
|
}
|
|
776
|
-
const e =
|
|
777
|
-
this.#u = this.#i !== null && this.#i >= e && this.#i <= s, this.#
|
|
778
|
-
const r = this.#
|
|
782
|
+
const e = v(t.from), s = v(t.to);
|
|
783
|
+
this.#u = this.#i !== null && this.#i >= e && this.#i <= s, this.#L = !1;
|
|
784
|
+
const r = this.#C({ from: e, to: s }, { emitChange: !0 });
|
|
779
785
|
if (i?.gesture) {
|
|
780
|
-
r && this.#
|
|
786
|
+
r && this.#Bt();
|
|
781
787
|
return;
|
|
782
788
|
}
|
|
783
|
-
this.#
|
|
789
|
+
this.#Y();
|
|
784
790
|
}
|
|
785
791
|
getYRange() {
|
|
786
|
-
return this.#
|
|
792
|
+
return this.#O;
|
|
787
793
|
}
|
|
788
794
|
getCrosshairPosition() {
|
|
789
795
|
return this.#d;
|
|
@@ -836,24 +842,24 @@ class qt extends at {
|
|
|
836
842
|
else if (e !== null)
|
|
837
843
|
s = e.y;
|
|
838
844
|
else {
|
|
839
|
-
const
|
|
840
|
-
s = (
|
|
845
|
+
const a = this.#O;
|
|
846
|
+
s = (a.min + a.max) / 2;
|
|
841
847
|
}
|
|
842
848
|
if (!Number.isFinite(s) || e !== null && e.time === i && e.y === s) return;
|
|
843
849
|
const r = this.timeScale.timeToX(i), n = this.yScale.valueToY(s);
|
|
844
|
-
this.#
|
|
850
|
+
this.#X = "time", this.#d = { mediaX: r, mediaY: n, time: i, y: s }, this.#h.markDirty(), this.emit("crosshairMove", this.#d);
|
|
845
851
|
}
|
|
846
852
|
/** Get the last visible value and whether the absolute last point is on screen. */
|
|
847
853
|
getLastValue(t) {
|
|
848
|
-
return
|
|
854
|
+
return G(t, this.#t, this.#s.getAnimationState().xRange);
|
|
849
855
|
}
|
|
850
856
|
/** Get the second-to-last value, useful for computing change. */
|
|
851
857
|
getPreviousClose(t) {
|
|
852
|
-
return
|
|
858
|
+
return U(t, this.#t);
|
|
853
859
|
}
|
|
854
860
|
getLastData(t) {
|
|
855
861
|
const i = this.#t.find((e) => e.id === t);
|
|
856
|
-
return !i || !
|
|
862
|
+
return !i || !x(i.renderer) ? null : i.renderer.getLastDataPoint();
|
|
857
863
|
}
|
|
858
864
|
/** Find the data point closest to the given timestamp within one data interval. */
|
|
859
865
|
getDataAtTime(t, i) {
|
|
@@ -871,7 +877,7 @@ class qt extends at {
|
|
|
871
877
|
return e?.visible ? e.renderer.getLayerSnapshots?.(i, this.#r) ?? null : null;
|
|
872
878
|
}
|
|
873
879
|
getSeriesIds() {
|
|
874
|
-
return this.#
|
|
880
|
+
return this.#D || (this.#D = this.#t.map((t) => t.id)), this.#D.slice();
|
|
875
881
|
}
|
|
876
882
|
/**
|
|
877
883
|
* True if any visible series has a time axis (line / bar / candlestick).
|
|
@@ -879,12 +885,31 @@ class qt extends at {
|
|
|
879
885
|
* has no meaningful x/y coordinate system, so the dashed hairlines would
|
|
880
886
|
* just be visual noise.
|
|
881
887
|
*/
|
|
882
|
-
#
|
|
888
|
+
#yt() {
|
|
883
889
|
for (const t of this.#t)
|
|
884
|
-
if (t.visible &&
|
|
890
|
+
if (t.visible && x(t.renderer))
|
|
885
891
|
return !0;
|
|
886
892
|
return !1;
|
|
887
893
|
}
|
|
894
|
+
/**
|
|
895
|
+
* Whether pan/zoom gestures currently apply — true when any visible series
|
|
896
|
+
* has a time axis. Part of the {@link PanZoomTarget} surface: on a
|
|
897
|
+
* spatial-only chart (pie, heatmap) the interaction layer stops capturing
|
|
898
|
+
* wheel and touch events, so the page scrolls normally under the cursor.
|
|
899
|
+
*/
|
|
900
|
+
canPanZoom() {
|
|
901
|
+
return this.#yt();
|
|
902
|
+
}
|
|
903
|
+
/**
|
|
904
|
+
* Push the current pan/zoom capability into the interaction layer (canvas
|
|
905
|
+
* `touch-action`). Wheel and mouse gates read {@link canPanZoom} live, but
|
|
906
|
+
* the CSS must be correct *before* a touch gesture starts — call this from
|
|
907
|
+
* every mutation that can flip {@link canPanZoom}: series add/remove and
|
|
908
|
+
* series visibility.
|
|
909
|
+
*/
|
|
910
|
+
#j() {
|
|
911
|
+
this.#T?.syncPanZoomMode();
|
|
912
|
+
}
|
|
888
913
|
/**
|
|
889
914
|
* Filter `getSeriesIds()` by renderer type. A concrete kind (`'pie'`,
|
|
890
915
|
* `'heatmap'`, a custom kind, …) returns series of that exact kind;
|
|
@@ -899,19 +924,19 @@ class qt extends at {
|
|
|
899
924
|
getSeriesIdsByType(t, i) {
|
|
900
925
|
const e = i?.visibleOnly === !0, s = i?.singleLayerOnly === !0, r = [];
|
|
901
926
|
for (const n of this.#t) {
|
|
902
|
-
if (!(t === "time" ?
|
|
903
|
-
const
|
|
904
|
-
if (!(s &&
|
|
927
|
+
if (!(t === "time" ? x(n.renderer) : n.renderer.kind === t)) continue;
|
|
928
|
+
const a = n.renderer.getLayerCount();
|
|
929
|
+
if (!(s && a > 1)) {
|
|
905
930
|
if (e) {
|
|
906
931
|
if (!n.visible) continue;
|
|
907
|
-
if (
|
|
908
|
-
let
|
|
909
|
-
for (let l = 0; l <
|
|
932
|
+
if (a > 1) {
|
|
933
|
+
let h = !1;
|
|
934
|
+
for (let l = 0; l < a; l++)
|
|
910
935
|
if (n.renderer.isLayerVisible(l)) {
|
|
911
|
-
|
|
936
|
+
h = !0;
|
|
912
937
|
break;
|
|
913
938
|
}
|
|
914
|
-
if (!
|
|
939
|
+
if (!h) continue;
|
|
915
940
|
}
|
|
916
941
|
}
|
|
917
942
|
r.push(n.id);
|
|
@@ -926,7 +951,7 @@ class qt extends at {
|
|
|
926
951
|
* Returns `null` for unknown ids or empty series.
|
|
927
952
|
*/
|
|
928
953
|
getStackedLastValue(t) {
|
|
929
|
-
return
|
|
954
|
+
return K(t, this.#t, this.#s.getAnimationState().xRange);
|
|
930
955
|
}
|
|
931
956
|
/**
|
|
932
957
|
* Stacked value at an arbitrary `time` — the cumulative stack top at that
|
|
@@ -983,7 +1008,7 @@ class qt extends at {
|
|
|
983
1008
|
*/
|
|
984
1009
|
setSeriesLabels(t, i) {
|
|
985
1010
|
const e = this.#t.find((s) => s.id === t);
|
|
986
|
-
!e ||
|
|
1011
|
+
!e || W(e.labels, i) || (e.labels = i, this.#p());
|
|
987
1012
|
}
|
|
988
1013
|
/**
|
|
989
1014
|
* Set the per-layer color overrides carried in `data` (sparse — `undefined`
|
|
@@ -994,7 +1019,7 @@ class qt extends at {
|
|
|
994
1019
|
*/
|
|
995
1020
|
setLayerColors(t, i) {
|
|
996
1021
|
const e = this.#t.find((s) => s.id === t);
|
|
997
|
-
!e ||
|
|
1022
|
+
!e || Mt(e.colorOverrides, i) || (e.colorOverrides = i, e.renderer.setColorOverrides?.(i), this.#e.markDirty(), this.#p());
|
|
998
1023
|
}
|
|
999
1024
|
/** Get per-layer colors for a series. Returns null for single-layer non-bar/line series. */
|
|
1000
1025
|
getSeriesLayers(t) {
|
|
@@ -1041,7 +1066,7 @@ class qt extends at {
|
|
|
1041
1066
|
/** Update axis configuration and re-render. */
|
|
1042
1067
|
setAxis(t) {
|
|
1043
1068
|
const i = this.yAxisWidth, e = this.xAxisHeight;
|
|
1044
|
-
this.#
|
|
1069
|
+
this.#W = t, this.#l = { min: t.y?.min, max: t.y?.max }, this.#f = !0;
|
|
1045
1070
|
const s = performance.now();
|
|
1046
1071
|
this.#s.onAxisReconfig(s), this.#v(s), (this.yAxisWidth !== i || this.xAxisHeight !== e) && this.syncScales(), this.#e.markDirty();
|
|
1047
1072
|
}
|
|
@@ -1085,13 +1110,13 @@ class qt extends at {
|
|
|
1085
1110
|
*/
|
|
1086
1111
|
setPadding(t) {
|
|
1087
1112
|
const i = this.#c;
|
|
1088
|
-
this.#c =
|
|
1089
|
-
const e = this.#c, s = !
|
|
1113
|
+
this.#c = I(t);
|
|
1114
|
+
const e = this.#c, s = !P(i.left, e.left) || !P(i.right, e.right), r = i.top !== e.top || i.bottom !== e.bottom;
|
|
1090
1115
|
if (s) {
|
|
1091
|
-
const { first: n, last:
|
|
1092
|
-
if (n !== void 0 &&
|
|
1093
|
-
const
|
|
1094
|
-
this.#
|
|
1116
|
+
const { first: n, last: a } = this.getDataBounds();
|
|
1117
|
+
if (n !== void 0 && a !== void 0) {
|
|
1118
|
+
const h = this.#a.size.media.width - this.yAxisWidth;
|
|
1119
|
+
this.#z(n, a, h);
|
|
1095
1120
|
}
|
|
1096
1121
|
}
|
|
1097
1122
|
if (s || r) {
|
|
@@ -1103,7 +1128,18 @@ class qt extends at {
|
|
|
1103
1128
|
}
|
|
1104
1129
|
/** Show or hide the background grid. Takes effect on the next render frame. */
|
|
1105
1130
|
setGrid(t) {
|
|
1106
|
-
this.#
|
|
1131
|
+
this.#V = t.visible, this.#e.markDirty();
|
|
1132
|
+
}
|
|
1133
|
+
/**
|
|
1134
|
+
* Set or clear the edge fade masks (see {@link ChartOptions.fade}).
|
|
1135
|
+
* Content the main layer draws inside a zone dissolves toward that edge.
|
|
1136
|
+
* `right` omitted re-arms its default (track the Y-axis width); pass
|
|
1137
|
+
* `{ top: 0, right: 0, left: 0 }` to turn every mask off. Takes effect on
|
|
1138
|
+
* the next render frame.
|
|
1139
|
+
*/
|
|
1140
|
+
setFade(t) {
|
|
1141
|
+
const i = C(t), e = this.#k;
|
|
1142
|
+
i.top === e.top && i.right === e.right && i.left === e.left || (this.#k = i, this.#e.markDirty());
|
|
1107
1143
|
}
|
|
1108
1144
|
/**
|
|
1109
1145
|
* Set the visual state for one side of the chart. Typically called in
|
|
@@ -1115,23 +1151,23 @@ class qt extends at {
|
|
|
1115
1151
|
* symmetry and currently renders identically to `idle`.
|
|
1116
1152
|
*/
|
|
1117
1153
|
setEdgeState(t, i) {
|
|
1118
|
-
if (this.#
|
|
1119
|
-
const e = this.#
|
|
1120
|
-
this.#
|
|
1154
|
+
if (this.#x[t] === i) return;
|
|
1155
|
+
const e = this.#x[t] === "loading", s = this.#I[t];
|
|
1156
|
+
this.#I[t] = !1, this.#B[t] = e && !s && (i === "idle" || i === "has-more") ? performance.now() : null, this.#x[t] = i, this.#h.markDirty();
|
|
1121
1157
|
}
|
|
1122
1158
|
/** Read the current host-declared state for a given edge. */
|
|
1123
1159
|
getEdgeState(t) {
|
|
1124
|
-
return this.#
|
|
1160
|
+
return this.#x[t];
|
|
1125
1161
|
}
|
|
1126
1162
|
/**
|
|
1127
1163
|
* Alpha for a side's `loading` exit fade, or `null` when none is in
|
|
1128
1164
|
* flight. Self-cleaning: an elapsed fade clears its start stamp.
|
|
1129
1165
|
*/
|
|
1130
|
-
#
|
|
1131
|
-
const i = this.#
|
|
1166
|
+
#vt(t) {
|
|
1167
|
+
const i = this.#B[t];
|
|
1132
1168
|
if (i === null) return null;
|
|
1133
|
-
const e = (performance.now() - i) /
|
|
1134
|
-
return e >= 1 ? (this.#
|
|
1169
|
+
const e = (performance.now() - i) / yt;
|
|
1170
|
+
return e >= 1 ? (this.#B[t] = null, null) : 1 - R(Math.max(0, e));
|
|
1135
1171
|
}
|
|
1136
1172
|
/**
|
|
1137
1173
|
* Swap the painter used for a side's `loading` spinner — the same
|
|
@@ -1140,7 +1176,7 @@ class qt extends at {
|
|
|
1140
1176
|
* immediately if that side is currently in the `loading` state.
|
|
1141
1177
|
*/
|
|
1142
1178
|
setEdgeIndicator(t, i) {
|
|
1143
|
-
this.#
|
|
1179
|
+
this.#lt[t] = i, this.#x[t] === "loading" && this.#h.markDirty();
|
|
1144
1180
|
}
|
|
1145
1181
|
/**
|
|
1146
1182
|
* Resolve the boundary point's Y position and series kind on the primary
|
|
@@ -1150,14 +1186,14 @@ class qt extends at {
|
|
|
1150
1186
|
* `open` (the value immediately preceding the boundary); `right` from the
|
|
1151
1187
|
* last visible candle's `close`.
|
|
1152
1188
|
*/
|
|
1153
|
-
#
|
|
1189
|
+
#Ht(t, i) {
|
|
1154
1190
|
const e = this.getSeriesIdsByType("time", { visibleOnly: !0, singleLayerOnly: !0 })[0] ?? this.getSeriesIdsByType("time", { visibleOnly: !0 })[0];
|
|
1155
1191
|
if (!e) return null;
|
|
1156
1192
|
const s = this.getDataAtTime(e, i);
|
|
1157
1193
|
if (!s) return null;
|
|
1158
1194
|
const r = this.#t.find((n) => n.id === e);
|
|
1159
1195
|
return r ? {
|
|
1160
|
-
valueY: this.yScale.valueToY(
|
|
1196
|
+
valueY: this.yScale.valueToY(X(t, s)),
|
|
1161
1197
|
seriesKind: r.renderer.kind
|
|
1162
1198
|
} : null;
|
|
1163
1199
|
}
|
|
@@ -1167,7 +1203,7 @@ class qt extends at {
|
|
|
1167
1203
|
* placeholder shapes to match the real bars that will render once data
|
|
1168
1204
|
* lands. `null` when the viewport has no meaningful span yet.
|
|
1169
1205
|
*/
|
|
1170
|
-
#
|
|
1206
|
+
#zt() {
|
|
1171
1207
|
const t = this.timeScale.barWidthMedia(this.#r);
|
|
1172
1208
|
return Number.isFinite(t) && t > 0 ? t : null;
|
|
1173
1209
|
}
|
|
@@ -1186,20 +1222,20 @@ class qt extends at {
|
|
|
1186
1222
|
* probed, not kind-gated: a kind check here silently kills hover for the
|
|
1187
1223
|
* next spatial renderer.
|
|
1188
1224
|
*/
|
|
1189
|
-
#
|
|
1225
|
+
#q(t) {
|
|
1190
1226
|
const i = this.#a.size, e = this.#c, s = {
|
|
1191
1227
|
top: e.top * i.verticalPixelRatio,
|
|
1192
1228
|
bottom: e.bottom * i.verticalPixelRatio
|
|
1193
1229
|
};
|
|
1194
1230
|
let r = !1, n = null;
|
|
1195
|
-
for (const
|
|
1196
|
-
if (!
|
|
1197
|
-
let
|
|
1231
|
+
for (const a of this.#t) {
|
|
1232
|
+
if (!a.renderer.hitTest || !a.renderer.setHoverIndex) continue;
|
|
1233
|
+
let h = -1;
|
|
1198
1234
|
if (t) {
|
|
1199
1235
|
const l = t.mediaX * i.horizontalPixelRatio, o = t.mediaY * i.verticalPixelRatio;
|
|
1200
|
-
|
|
1236
|
+
h = a.renderer.hitTest(l, o, i.bitmap.width, i.bitmap.height, s);
|
|
1201
1237
|
}
|
|
1202
|
-
|
|
1238
|
+
a.renderer.setHoverIndex(h) && (r = !0), h >= 0 && !n && (n = { seriesId: a.id, index: h, info: a.renderer.getHoverInfo?.(this.#n) ?? null });
|
|
1203
1239
|
}
|
|
1204
1240
|
r && (this.#e.markDirty(), this.emit("seriesHover", n));
|
|
1205
1241
|
}
|
|
@@ -1210,15 +1246,15 @@ class qt extends at {
|
|
|
1210
1246
|
* Returns the first spatial series (pie, heatmap, or a custom spatial kind)
|
|
1211
1247
|
* whose `hitTest` claims the position, or `null` if none does.
|
|
1212
1248
|
*/
|
|
1213
|
-
#
|
|
1249
|
+
#bt(t) {
|
|
1214
1250
|
const i = this.#a.size, e = this.#c, s = {
|
|
1215
1251
|
top: e.top * i.verticalPixelRatio,
|
|
1216
1252
|
bottom: e.bottom * i.verticalPixelRatio
|
|
1217
1253
|
}, r = t.mediaX * i.horizontalPixelRatio, n = t.mediaY * i.verticalPixelRatio;
|
|
1218
|
-
for (const
|
|
1219
|
-
if (!
|
|
1220
|
-
const
|
|
1221
|
-
if (
|
|
1254
|
+
for (const a of this.#t) {
|
|
1255
|
+
if (!a.renderer.hitTest) continue;
|
|
1256
|
+
const h = a.renderer.hitTest(r, n, i.bitmap.width, i.bitmap.height, s);
|
|
1257
|
+
if (h >= 0) return { seriesId: a.id, index: h };
|
|
1222
1258
|
}
|
|
1223
1259
|
return null;
|
|
1224
1260
|
}
|
|
@@ -1242,40 +1278,40 @@ class qt extends at {
|
|
|
1242
1278
|
* viewport didn't move, the re-derived values are bit-identical and the
|
|
1243
1279
|
* method exits without emitting.
|
|
1244
1280
|
*/
|
|
1245
|
-
#
|
|
1281
|
+
#Vt() {
|
|
1246
1282
|
const t = this.#d;
|
|
1247
1283
|
if (t === null) return;
|
|
1248
|
-
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,
|
|
1249
|
-
let
|
|
1250
|
-
if (this.#
|
|
1284
|
+
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;
|
|
1285
|
+
let h;
|
|
1286
|
+
if (this.#X === "pointer") {
|
|
1251
1287
|
const l = this.timeScale.xToTime(t.mediaX), o = this.yScale.yToValue(t.mediaY);
|
|
1252
1288
|
if (!Number.isFinite(l) || !Number.isFinite(o) || Math.abs(l - t.time) <= r && Math.abs(o - t.y) <= n) return;
|
|
1253
|
-
|
|
1289
|
+
h = { mediaX: t.mediaX, mediaY: t.mediaY, time: l, y: o };
|
|
1254
1290
|
} else {
|
|
1255
1291
|
if (t.time < i || t.time > e) {
|
|
1256
|
-
this.#d = null, this.#h.markDirty(), this.emit("crosshairMove", null), this.#
|
|
1292
|
+
this.#d = null, this.#h.markDirty(), this.emit("crosshairMove", null), this.#q(null);
|
|
1257
1293
|
return;
|
|
1258
1294
|
}
|
|
1259
1295
|
const l = this.timeScale.timeToX(t.time), o = this.yScale.valueToY(t.y);
|
|
1260
|
-
if (!Number.isFinite(l) || !Number.isFinite(o) || Math.abs(l - t.mediaX) <=
|
|
1261
|
-
|
|
1296
|
+
if (!Number.isFinite(l) || !Number.isFinite(o) || Math.abs(l - t.mediaX) <= a && Math.abs(o - t.mediaY) <= a) return;
|
|
1297
|
+
h = { mediaX: l, mediaY: o, time: t.time, y: t.y };
|
|
1262
1298
|
}
|
|
1263
|
-
this.#d =
|
|
1299
|
+
this.#d = h, this.#h.markDirty(), this.emit("crosshairMove", h), this.#q(h);
|
|
1264
1300
|
}
|
|
1265
1301
|
/** Tear down the chart: cancel animations, remove listeners, and detach the canvas. */
|
|
1266
1302
|
destroy() {
|
|
1267
1303
|
for (const t of this.#t) t.renderer.dispose();
|
|
1268
|
-
this.#t = [], this.#m = [], this.#b = [], this.#y = [], this.#
|
|
1304
|
+
this.#t = [], this.#m = [], this.#b = [], this.#y = [], this.#D = null, this.#e.destroy(), this.#h.destroy(), this.#T?.destroy(), this.#a.destroy(), this.#U?.destroy(), this.#U = null, this.#G && this.#S?.destroy(), this.#S = null, this.#G = !1, this.removeAllListeners();
|
|
1269
1305
|
}
|
|
1270
1306
|
/** The attached performance monitor, or `null` when instrumentation is disabled. */
|
|
1271
1307
|
getPerfMonitor() {
|
|
1272
|
-
return this.#
|
|
1308
|
+
return this.#S;
|
|
1273
1309
|
}
|
|
1274
1310
|
/** Compute the earliest and latest timestamps across all series. */
|
|
1275
1311
|
getDataBounds() {
|
|
1276
1312
|
let t, i;
|
|
1277
1313
|
for (const e of this.#t) {
|
|
1278
|
-
if (!e.visible || !
|
|
1314
|
+
if (!e.visible || !x(e.renderer)) continue;
|
|
1279
1315
|
const s = e.renderer.getTimeBounds();
|
|
1280
1316
|
s !== null && ((t === void 0 || s.first < t) && (t = s.first), (i === void 0 || s.last > i) && (i = s.last));
|
|
1281
1317
|
}
|
|
@@ -1284,28 +1320,28 @@ class qt extends at {
|
|
|
1284
1320
|
/** Set by `setSeriesData`/bulk-replace paths so the next `onDataChanged`
|
|
1285
1321
|
* snaps the Y range (instead of easing) and `yScale.getRange()` reflects
|
|
1286
1322
|
* the new domain synchronously. Cleared by `onDataChanged` after each run. */
|
|
1287
|
-
#
|
|
1323
|
+
#J = !1;
|
|
1288
1324
|
onDataChanged() {
|
|
1289
|
-
if (this.#
|
|
1290
|
-
this
|
|
1325
|
+
if (this.#M > 0) {
|
|
1326
|
+
this.#$ = !0;
|
|
1291
1327
|
return;
|
|
1292
1328
|
}
|
|
1293
1329
|
this.updateDataInterval();
|
|
1294
1330
|
const { first: t, last: i } = this.getDataBounds();
|
|
1295
|
-
t !== void 0 && (this.#
|
|
1296
|
-
const e = this.#
|
|
1297
|
-
this.#
|
|
1331
|
+
t !== void 0 && (this.#A = t), i !== void 0 && (this.#F = this.#i, this.#i = i);
|
|
1332
|
+
const e = this.#J;
|
|
1333
|
+
this.#J = !1;
|
|
1298
1334
|
const s = !this.#f && t !== void 0;
|
|
1299
|
-
e && (this.yScale.tickTracker.reset(), this.timeScale.tickTracker.reset()), this.#
|
|
1335
|
+
e && (this.yScale.tickTracker.reset(), this.timeScale.tickTracker.reset()), this.#dt = e;
|
|
1300
1336
|
const r = performance.now();
|
|
1301
|
-
e || s ? (this.#
|
|
1337
|
+
e || s ? (this.#R.seed(r), this.#s.onDataReplaced(r)) : this.#s.onPointAppended(r), this.#s.lastXTarget !== null && (this.#H = !0), t !== void 0 && (this.#f = !0), this.#v(r), this.syncScales(), s ? this.#et() : this.#e.markDirty(), this.emit("dataUpdate"), this.#p();
|
|
1302
1338
|
}
|
|
1303
1339
|
updateDataInterval() {
|
|
1304
1340
|
for (const t of this.#t) {
|
|
1305
|
-
if (!
|
|
1341
|
+
if (!x(t.renderer)) continue;
|
|
1306
1342
|
const i = t.renderer.sampleTimes(20);
|
|
1307
1343
|
if (i.length >= 2) {
|
|
1308
|
-
this.#r =
|
|
1344
|
+
this.#r = pt(i);
|
|
1309
1345
|
break;
|
|
1310
1346
|
}
|
|
1311
1347
|
}
|
|
@@ -1321,22 +1357,22 @@ class qt extends at {
|
|
|
1321
1357
|
* range — without this guard the engine would ease from `{0,0}` into
|
|
1322
1358
|
* the new target and the very first frame would render with a
|
|
1323
1359
|
* zero-width X domain. */
|
|
1324
|
-
#
|
|
1360
|
+
#H = !1;
|
|
1325
1361
|
/**
|
|
1326
1362
|
* One-shot `setVisibleRange` argument applied on the first data arrival.
|
|
1327
1363
|
* Captured at chart construction from {@link ChartOptions.viewport.initialRange};
|
|
1328
1364
|
* cleared the moment it's consumed so subsequent data swaps don't snap
|
|
1329
1365
|
* back to the initial window.
|
|
1330
1366
|
*/
|
|
1331
|
-
#
|
|
1367
|
+
#E;
|
|
1332
1368
|
/** Last Y bounds the engine resolved into {@link AnimationState.yRange}.
|
|
1333
1369
|
* Used by {@link renderMain} to detect Y movement frame-to-frame so
|
|
1334
1370
|
* `viewportChange` only emits when the Y range actually shifted — DOM
|
|
1335
1371
|
* axis labels subscribe to that event and re-renders on every tick are
|
|
1336
1372
|
* expensive. Initialised to NaN so the first real Y emit always passes
|
|
1337
1373
|
* the change check. */
|
|
1338
|
-
#
|
|
1339
|
-
#
|
|
1374
|
+
#Q = Number.NaN;
|
|
1375
|
+
#tt = Number.NaN;
|
|
1340
1376
|
/**
|
|
1341
1377
|
* Sample the visible data window and return the resolved Y bounds, or
|
|
1342
1378
|
* `null` when no series has data inside the X destination range.
|
|
@@ -1345,11 +1381,11 @@ class qt extends at {
|
|
|
1345
1381
|
* converge together. Clears {@link #yInited} on empty so the next emit
|
|
1346
1382
|
* with valid data lands as an instant snap.
|
|
1347
1383
|
*/
|
|
1348
|
-
#
|
|
1349
|
-
const i = t ?? this.#o, e = this.#l.min !== void 0 && this.#l.min !== "auto" && typeof this.#l.min != "number" || this.#l.max !== void 0 && this.#l.max !== "auto" && typeof this.#l.max != "number" ? [] : null, s =
|
|
1384
|
+
#xt(t) {
|
|
1385
|
+
const i = t ?? this.#o, e = this.#l.min !== void 0 && this.#l.min !== "auto" && typeof this.#l.min != "number" || this.#l.max !== void 0 && this.#l.max !== "auto" && typeof this.#l.max != "number" ? [] : null, s = et(i, this.#t, e);
|
|
1350
1386
|
if (s === null)
|
|
1351
1387
|
return this.#f = !1, null;
|
|
1352
|
-
const r =
|
|
1388
|
+
const r = z(this.#l.min, s.min, s.max, e ?? [], "min"), n = z(this.#l.max, s.max, s.min, e ?? [], "max");
|
|
1353
1389
|
return { min: r, max: n };
|
|
1354
1390
|
}
|
|
1355
1391
|
/**
|
|
@@ -1370,29 +1406,29 @@ class qt extends at {
|
|
|
1370
1406
|
* - **autoscroll off** → `null` (no X retarget; user has panned away
|
|
1371
1407
|
* from the tail).
|
|
1372
1408
|
*/
|
|
1373
|
-
#
|
|
1374
|
-
if (this.#
|
|
1409
|
+
#Wt() {
|
|
1410
|
+
if (this.#A === null || this.#i === null) return null;
|
|
1375
1411
|
const { from: t, to: i } = this.#o, e = t === 0 && i === 0, s = this.#a.size.media.width - this.yAxisWidth;
|
|
1376
1412
|
if (e) {
|
|
1377
|
-
if (this.#
|
|
1413
|
+
if (this.#z(this.#A, this.#i, s), this.#E !== void 0 && (this.setVisibleRange(this.#E), this.#E = void 0, !this.#u && this.#i !== null)) {
|
|
1378
1414
|
const { to: n } = this.#o;
|
|
1379
1415
|
this.#i >= n && this.#i - n <= this.#r * 1.5 && (this.#u = !0);
|
|
1380
1416
|
}
|
|
1381
1417
|
return this.#o;
|
|
1382
1418
|
}
|
|
1383
1419
|
if (!this.#u) return null;
|
|
1384
|
-
if (this.#
|
|
1385
|
-
return this.#
|
|
1386
|
-
const r =
|
|
1420
|
+
if (this.#dt)
|
|
1421
|
+
return this.#z(this.#A, this.#i, s), this.#o;
|
|
1422
|
+
const r = it({
|
|
1387
1423
|
currentLogical: this.#o,
|
|
1388
1424
|
lastTime: this.#i,
|
|
1389
|
-
prevDataEnd: this.#
|
|
1425
|
+
prevDataEnd: this.#F,
|
|
1390
1426
|
dataInterval: this.#r,
|
|
1391
1427
|
paddingRight: this.#c.right,
|
|
1392
1428
|
chartWidth: s,
|
|
1393
|
-
holdUntilFilled: this.#
|
|
1429
|
+
holdUntilFilled: this.#L
|
|
1394
1430
|
});
|
|
1395
|
-
return r.releaseHold && (this.#
|
|
1431
|
+
return r.releaseHold && (this.#L = !1), r.reengageAutoScroll && (this.#u = !0), r.newLogical === null ? null : (this.#R.observe(performance.now()), this.#s.setXSettleMs(this.#R.pickSettleMs(this.#g.axis.x.settleMs)), this.#C(r.newLogical, { emitChange: !1, skipValidation: !0 }), this.#F = this.#i, r.newLogical);
|
|
1396
1432
|
}
|
|
1397
1433
|
/**
|
|
1398
1434
|
* Fit the viewport's logical X range around a data span. Mirrors the
|
|
@@ -1400,16 +1436,16 @@ class qt extends at {
|
|
|
1400
1436
|
* forces autoscroll on (data tail is in the new window by construction),
|
|
1401
1437
|
* commits the new range via `viewport.setRange` (which emits `change`).
|
|
1402
1438
|
*/
|
|
1403
|
-
#
|
|
1404
|
-
const s = this.#c, r =
|
|
1439
|
+
#z(t, i, e) {
|
|
1440
|
+
const s = this.#c, r = _({
|
|
1405
1441
|
firstTime: t,
|
|
1406
1442
|
lastTime: i,
|
|
1407
1443
|
dataInterval: this.#r,
|
|
1408
|
-
maxVisibleBars: this.#
|
|
1444
|
+
maxVisibleBars: this.#mt,
|
|
1409
1445
|
chartWidth: e,
|
|
1410
1446
|
padding: { left: s.left, right: s.right }
|
|
1411
1447
|
});
|
|
1412
|
-
this.#
|
|
1448
|
+
this.#L = !1, this.#u = !0, this.#C(r, { emitChange: !0 });
|
|
1413
1449
|
}
|
|
1414
1450
|
/**
|
|
1415
1451
|
* Commit a programmatic zoom — `fitContent` or `setVisibleRange`. Reads
|
|
@@ -1425,12 +1461,12 @@ class qt extends at {
|
|
|
1425
1461
|
* passes `xEase: true` so X eases instead — viewport and axis converge
|
|
1426
1462
|
* on the same frame.
|
|
1427
1463
|
*/
|
|
1428
|
-
#
|
|
1464
|
+
#Y(t) {
|
|
1429
1465
|
const i = this.#o;
|
|
1430
1466
|
if (i.to <= i.from) return;
|
|
1431
1467
|
const e = performance.now();
|
|
1432
|
-
if (!this.#
|
|
1433
|
-
this.#
|
|
1468
|
+
if (!this.#H || !this.#f) {
|
|
1469
|
+
this.#St(e);
|
|
1434
1470
|
return;
|
|
1435
1471
|
}
|
|
1436
1472
|
this.#s.onProgrammaticZoom({ xTarget: i, xEase: t?.xEase }, e), this.#v(e);
|
|
@@ -1447,12 +1483,12 @@ class qt extends at {
|
|
|
1447
1483
|
* instead of lagging on the long sticky contract. First paint still snaps
|
|
1448
1484
|
* (same as the programmatic path) so synchronous readers see final values.
|
|
1449
1485
|
*/
|
|
1450
|
-
#
|
|
1486
|
+
#Bt() {
|
|
1451
1487
|
const t = this.#o;
|
|
1452
1488
|
if (t.to <= t.from) return;
|
|
1453
1489
|
const i = performance.now();
|
|
1454
|
-
if (!this.#
|
|
1455
|
-
this.#
|
|
1490
|
+
if (!this.#H || !this.#f) {
|
|
1491
|
+
this.#St(i);
|
|
1456
1492
|
return;
|
|
1457
1493
|
}
|
|
1458
1494
|
this.#s.onPanZoom({ xTarget: t, yAuto: !0 }, i), this.#v(i);
|
|
@@ -1464,9 +1500,9 @@ class qt extends at {
|
|
|
1464
1500
|
* commits — both must snap before the chart has painted once so a
|
|
1465
1501
|
* synchronous `getYRange()` / `getVisibleRange()` read sees final values.
|
|
1466
1502
|
*/
|
|
1467
|
-
#
|
|
1468
|
-
const i = this.#o, e = this.#
|
|
1469
|
-
this.#
|
|
1503
|
+
#St(t) {
|
|
1504
|
+
const i = this.#o, e = this.#xt(i);
|
|
1505
|
+
this.#H = !0, e !== null && (this.#f = !0), this.#s.snap({ x: i, y: e ?? void 0 }, t), this.#v(t);
|
|
1470
1506
|
}
|
|
1471
1507
|
/**
|
|
1472
1508
|
* Validate and commit a new logical X range. Mirrors the old
|
|
@@ -1486,7 +1522,7 @@ class qt extends at {
|
|
|
1486
1522
|
* see `setVisibleRange`'s gesture path, where a per-frame echo would
|
|
1487
1523
|
* otherwise re-arm and stall the in-flight Y ease.
|
|
1488
1524
|
*/
|
|
1489
|
-
#
|
|
1525
|
+
#C(t, i) {
|
|
1490
1526
|
const { from: e, to: s } = t;
|
|
1491
1527
|
return !Number.isFinite(e) || !Number.isFinite(s) || s <= e || !i.skipValidation && (s - e) / this.#r < 2 || this.#o.from === e && this.#o.to === s ? !1 : (this.#o = { from: e, to: s }, i.emitChange && (this.syncScales(), this.#e?.markDirty(), this.emit("viewportChange")), !0);
|
|
1492
1528
|
}
|
|
@@ -1496,11 +1532,11 @@ class qt extends at {
|
|
|
1496
1532
|
* symmetric pad top / bottom from `#padding`, suppressed on the side
|
|
1497
1533
|
* that has an explicit (fixed) axis bound.
|
|
1498
1534
|
*/
|
|
1499
|
-
#
|
|
1500
|
-
const n = i - t,
|
|
1501
|
-
this.#
|
|
1502
|
-
min: s ? t : t -
|
|
1503
|
-
max: r ? i : i +
|
|
1535
|
+
#kt(t, i, e, s, r) {
|
|
1536
|
+
const n = i - t, a = e > 0 ? this.#c.top / e * n : 0, h = e > 0 ? this.#c.bottom / e * n : 0;
|
|
1537
|
+
this.#O = {
|
|
1538
|
+
min: s ? t : t - h,
|
|
1539
|
+
max: r ? i : i + a
|
|
1504
1540
|
};
|
|
1505
1541
|
}
|
|
1506
1542
|
/**
|
|
@@ -1513,18 +1549,18 @@ class qt extends at {
|
|
|
1513
1549
|
* as a gesture for visual easing. Public so consumers can drive pan
|
|
1514
1550
|
* programmatically.
|
|
1515
1551
|
*/
|
|
1516
|
-
pan(t, i = this.#
|
|
1517
|
-
i > 0 && (this.#
|
|
1518
|
-
const e =
|
|
1552
|
+
pan(t, i = this.#N) {
|
|
1553
|
+
i > 0 && (this.#N = i), this.#L = !1;
|
|
1554
|
+
const e = J({
|
|
1519
1555
|
currentLogical: this.#o,
|
|
1520
1556
|
timeDelta: t,
|
|
1521
1557
|
chartWidth: i,
|
|
1522
1558
|
dataInterval: this.#r,
|
|
1523
1559
|
padding: { left: this.#c.left, right: this.#c.right },
|
|
1524
|
-
dataStart: this.#
|
|
1560
|
+
dataStart: this.#A,
|
|
1525
1561
|
dataEnd: this.#i
|
|
1526
1562
|
});
|
|
1527
|
-
e.newLogical !== null && (this.#u = !e.autoScrollOff, this.#
|
|
1563
|
+
e.newLogical !== null && (this.#u = !e.autoScrollOff, this.#C(e.newLogical, { emitChange: !0 }), e.edgeReached !== null && (this.#ht[e.edgeReached.side] = e.edgeReached.boundaryTime, this.#ot?.(e.edgeReached)), this.#R.pause(), this.#wt());
|
|
1528
1564
|
}
|
|
1529
1565
|
/**
|
|
1530
1566
|
* Zoom around a time anchor. `factor < 1` zooms in, `> 1` zooms out.
|
|
@@ -1541,35 +1577,35 @@ class qt extends at {
|
|
|
1541
1577
|
* and produce a visible jump). Cursor-anchored zoom is intentionally
|
|
1542
1578
|
* sacrificed in follow-live mode — pan first to inspect history.
|
|
1543
1579
|
*/
|
|
1544
|
-
zoomAt(t, i, e = this.#
|
|
1545
|
-
e > 0 && (this.#
|
|
1546
|
-
const s =
|
|
1580
|
+
zoomAt(t, i, e = this.#N) {
|
|
1581
|
+
e > 0 && (this.#N = e), this.#L = !1;
|
|
1582
|
+
const s = Q({
|
|
1547
1583
|
currentLogical: this.#o,
|
|
1548
1584
|
centerTime: t,
|
|
1549
1585
|
factor: i,
|
|
1550
1586
|
chartWidth: e,
|
|
1551
1587
|
dataInterval: this.#r,
|
|
1552
1588
|
padding: { left: this.#c.left, right: this.#c.right },
|
|
1553
|
-
dataStart: this.#
|
|
1589
|
+
dataStart: this.#A,
|
|
1554
1590
|
dataEnd: this.#i
|
|
1555
1591
|
});
|
|
1556
1592
|
if (s.newLogical === null) return;
|
|
1557
1593
|
let r = s.newLogical;
|
|
1558
1594
|
if (this.#u && this.#i !== null) {
|
|
1559
|
-
const n = r.to - r.from,
|
|
1560
|
-
r = { from:
|
|
1595
|
+
const n = r.to - r.from, a = H(this.#c.right, n, this.#r, e), h = this.#i + a;
|
|
1596
|
+
r = { from: h - n, to: h }, this.#F = this.#i;
|
|
1561
1597
|
}
|
|
1562
|
-
this.#
|
|
1598
|
+
this.#C(r, { emitChange: !0 }), this.#R.pause(), this.#wt();
|
|
1563
1599
|
}
|
|
1564
1600
|
/**
|
|
1565
1601
|
* Common tail for `pan` / `zoomAt`: routes the just-committed `#logical`
|
|
1566
1602
|
* to the engine as a gesture so the X spring retargets and the Y spring
|
|
1567
1603
|
* eases over `animations.axis.y.gesture`.
|
|
1568
1604
|
*/
|
|
1569
|
-
#
|
|
1605
|
+
#wt() {
|
|
1570
1606
|
const t = this.#o;
|
|
1571
1607
|
if (t.to <= t.from) return;
|
|
1572
|
-
this.#
|
|
1608
|
+
this.#H = !0, this.#f = !0;
|
|
1573
1609
|
const i = performance.now();
|
|
1574
1610
|
this.#s.onPanZoom({ xTarget: t, yAuto: !0 }, i), this.#v(i);
|
|
1575
1611
|
}
|
|
@@ -1590,7 +1626,7 @@ class qt extends at {
|
|
|
1590
1626
|
const i = this.#s.tick(t), e = this.#a.size;
|
|
1591
1627
|
if (e.media.width === 0 || e.media.height === 0) return;
|
|
1592
1628
|
const s = this.#l.min !== void 0 && this.#l.min !== "auto", r = this.#l.max !== void 0 && this.#l.max !== "auto", n = e.media.height - this.xAxisHeight;
|
|
1593
|
-
this.#
|
|
1629
|
+
this.#kt(i.yRange.min, i.yRange.max, n, s, r), this.#Q = i.yRange.min, this.#tt = i.yRange.max, this.syncScales();
|
|
1594
1630
|
}
|
|
1595
1631
|
/**
|
|
1596
1632
|
* Lightweight scale sync: updates timeScale/yScale from current viewport state
|
|
@@ -1606,7 +1642,7 @@ class qt extends at {
|
|
|
1606
1642
|
i,
|
|
1607
1643
|
t.horizontalPixelRatio,
|
|
1608
1644
|
this.#r
|
|
1609
|
-
), this.yScale.update(this.#
|
|
1645
|
+
), this.yScale.update(this.#O, e, t.verticalPixelRatio);
|
|
1610
1646
|
}
|
|
1611
1647
|
/** Expensive: background, grid, all series. Only on data/viewport/resize change. */
|
|
1612
1648
|
renderMain(t) {
|
|
@@ -1614,64 +1650,231 @@ class qt extends at {
|
|
|
1614
1650
|
if (i.media.width === 0 || i.media.height === 0) return;
|
|
1615
1651
|
const e = typeof t == "number" ? t : performance.now(), s = this.#s.tick(e);
|
|
1616
1652
|
s.animating && this.#e.markDirty();
|
|
1617
|
-
const r = s.yRange.min, n = s.yRange.max,
|
|
1618
|
-
if (
|
|
1619
|
-
const
|
|
1620
|
-
this.#
|
|
1653
|
+
const r = s.yRange.min, n = s.yRange.max, a = r !== this.#Q || n !== this.#tt;
|
|
1654
|
+
if (a) {
|
|
1655
|
+
const c = this.#l.min !== void 0 && this.#l.min !== "auto", d = this.#l.max !== void 0 && this.#l.max !== "auto", p = i.media.height - this.xAxisHeight;
|
|
1656
|
+
this.#kt(r, n, p, c, d), this.#Q = r, this.#tt = n;
|
|
1621
1657
|
}
|
|
1622
|
-
if (this.syncScales(), this.#
|
|
1623
|
-
const
|
|
1624
|
-
if (
|
|
1625
|
-
const p = this.#a.size.media.width - this.yAxisWidth,
|
|
1626
|
-
Math.abs(
|
|
1658
|
+
if (this.syncScales(), this.#Vt(), this.#i !== null && !this.#u) {
|
|
1659
|
+
const c = this.#s.lastXTarget ?? this.#o, d = c.to - c.from;
|
|
1660
|
+
if (d > 0) {
|
|
1661
|
+
const p = this.#a.size.media.width - this.yAxisWidth, f = H(this.#c.right, d, this.#r, p), m = this.#r * 0.5;
|
|
1662
|
+
Math.abs(c.to - (this.#i + f)) < m && (this.#u = !0);
|
|
1627
1663
|
}
|
|
1628
1664
|
}
|
|
1629
|
-
|
|
1630
|
-
let
|
|
1631
|
-
this.#a.useMainLayer((
|
|
1632
|
-
const { context:
|
|
1633
|
-
|
|
1634
|
-
const
|
|
1635
|
-
|
|
1636
|
-
|
|
1665
|
+
a && this.emit("viewportChange");
|
|
1666
|
+
let h = !1, l = !1;
|
|
1667
|
+
this.#a.useMainLayer((c) => {
|
|
1668
|
+
const { context: d, bitmapSize: p } = c, f = (i.media.width - this.yAxisWidth) * i.horizontalPixelRatio, m = (i.media.height - this.xAxisHeight) * i.verticalPixelRatio;
|
|
1669
|
+
d.clearRect(0, 0, p.width, p.height), d.save(), d.beginPath();
|
|
1670
|
+
const y = this.#it(c, f);
|
|
1671
|
+
d.rect(0, 0, f + y.intrusion, m), d.clip(), this.yScale.tickTracker.setCurrentTicks(this.yScale.niceTickValues()), this.timeScale.tickTracker.setCurrentTicks(this.timeScale.niceTickValues(this.#r).ticks), this.yScale.tickTracker.tick(e), this.timeScale.tickTracker.tick(e);
|
|
1672
|
+
const w = this.yScale.tickTracker.snapshot(), S = this.timeScale.tickTracker.snapshot();
|
|
1673
|
+
h = w.isAnimating, l = S.isAnimating, (w.isAnimating || S.isAnimating || s.animating) && this.emit("tickFrame"), this.#V && rt({
|
|
1674
|
+
scope: c,
|
|
1637
1675
|
timeScale: this.timeScale,
|
|
1638
1676
|
yScale: this.yScale,
|
|
1639
1677
|
theme: this.#n,
|
|
1640
|
-
yTicks:
|
|
1641
|
-
timeTicks:
|
|
1642
|
-
}), this.#
|
|
1643
|
-
const
|
|
1678
|
+
yTicks: w,
|
|
1679
|
+
timeTicks: S
|
|
1680
|
+
}), this.#It(c, f, m);
|
|
1681
|
+
const M = this.#c, B = { top: M.top, bottom: M.bottom }, L = this.#S;
|
|
1644
1682
|
for (const b of this.#t) {
|
|
1645
|
-
const
|
|
1646
|
-
if (
|
|
1683
|
+
const k = b.renderer.getAlpha?.() ?? (b.visible ? 1 : 0);
|
|
1684
|
+
if (k <= 0) continue;
|
|
1647
1685
|
const T = {
|
|
1648
|
-
scope:
|
|
1686
|
+
scope: c,
|
|
1649
1687
|
timeScale: this.timeScale,
|
|
1650
1688
|
yScale: this.yScale,
|
|
1651
1689
|
theme: this.#n,
|
|
1652
1690
|
dataInterval: this.#r,
|
|
1653
|
-
padding:
|
|
1691
|
+
padding: B,
|
|
1654
1692
|
state: s,
|
|
1655
1693
|
seriesId: b.id
|
|
1656
|
-
}, D =
|
|
1657
|
-
if (
|
|
1658
|
-
const
|
|
1659
|
-
b.renderer.render(T),
|
|
1694
|
+
}, D = d.globalAlpha;
|
|
1695
|
+
if (k < 1 && (d.globalAlpha = D * k), L) {
|
|
1696
|
+
const F = performance.now();
|
|
1697
|
+
b.renderer.render(T), L.recordSeries(b.id, performance.now() - F, e);
|
|
1660
1698
|
} else
|
|
1661
1699
|
b.renderer.render(T);
|
|
1662
|
-
|
|
1700
|
+
k < 1 && (d.globalAlpha = D);
|
|
1663
1701
|
}
|
|
1664
|
-
|
|
1665
|
-
|
|
1702
|
+
d.restore(), this.#Ft({
|
|
1703
|
+
scope: c,
|
|
1704
|
+
paneBitmapWidth: f,
|
|
1705
|
+
paneBitmapHeight: m,
|
|
1706
|
+
timeTicks: S
|
|
1707
|
+
}), this.#At(c, m), this.#Lt(c, f);
|
|
1708
|
+
}), this.#K();
|
|
1666
1709
|
let o = !1;
|
|
1667
|
-
for (const
|
|
1668
|
-
if (
|
|
1710
|
+
for (const c of this.#t)
|
|
1711
|
+
if (c.renderer.needsAnimation) {
|
|
1669
1712
|
o = !0, this.#e.markDirty();
|
|
1670
1713
|
break;
|
|
1671
1714
|
}
|
|
1672
|
-
const u =
|
|
1715
|
+
const u = h || l;
|
|
1673
1716
|
!s.animating && !o && !u && (this.yScale.tickTracker.markArmed(), this.timeScale.tickTracker.markArmed());
|
|
1674
1717
|
}
|
|
1718
|
+
/**
|
|
1719
|
+
* Erase the main layer's alpha across the top fade zone so content
|
|
1720
|
+
* dissolves as it approaches the top edge. `destination-out` multiplies
|
|
1721
|
+
* the already-painted pixels by the mask's inverse alpha — an *erase*, not
|
|
1722
|
+
* a paint-over — so the container background behind the canvas (solid or
|
|
1723
|
+
* CSS gradient) shows through untouched.
|
|
1724
|
+
*/
|
|
1725
|
+
#At(t, i) {
|
|
1726
|
+
if (this.#k.top <= 0) return;
|
|
1727
|
+
const e = Math.min(Math.round(this.#k.top * t.verticalPixelRatio), Math.round(i));
|
|
1728
|
+
if (e < 1) return;
|
|
1729
|
+
const { context: s } = t;
|
|
1730
|
+
s.save(), s.globalCompositeOperation = "destination-out", s.fillStyle = this.#Nt(s, e), s.fillRect(0, 0, t.bitmapSize.width, e), s.restore();
|
|
1731
|
+
}
|
|
1732
|
+
/**
|
|
1733
|
+
* Run the vertical gridlines a short stub past the pane floor, dissolving
|
|
1734
|
+
* to transparent on the way to the time-axis labels — the bottom-edge
|
|
1735
|
+
* mirror of the horizontal gridlines melting under the Y-axis column.
|
|
1736
|
+
* Drawn outside the pane clip (the stubs live below the pane, where
|
|
1737
|
+
* nothing else paints) and faded with the same destination-out ramp the
|
|
1738
|
+
* edge masks use, so the taper stays correct over any background.
|
|
1739
|
+
*/
|
|
1740
|
+
#Ft(t) {
|
|
1741
|
+
if (!this.#V || this.xAxisHeight === 0) return;
|
|
1742
|
+
const { scope: i, paneBitmapWidth: e, paneBitmapHeight: s, timeTicks: r } = t, { context: n, horizontalPixelRatio: a, verticalPixelRatio: h } = i, l = Math.round(s), o = Math.round(A * h);
|
|
1743
|
+
if (o < 1) return;
|
|
1744
|
+
const u = this.#n.grid;
|
|
1745
|
+
n.save(), n.strokeStyle = u.color, u.style === "dashed" ? n.setLineDash([4 * a, 4 * a]) : u.style === "dotted" && n.setLineDash([1 * a, 3 * a]), n.lineDashOffset = l;
|
|
1746
|
+
const c = Math.max(1, Math.round(a)), d = c % 2 === 1 ? 0.5 : 0;
|
|
1747
|
+
n.lineWidth = c;
|
|
1748
|
+
const p = Math.round(e);
|
|
1749
|
+
for (const { value: f, opacity: m } of r.entries) {
|
|
1750
|
+
if (m <= 0.01) continue;
|
|
1751
|
+
const y = Math.round(this.timeScale.timeToBitmapX(f)) + d;
|
|
1752
|
+
y < 0 || y > p || (n.globalAlpha = m, n.beginPath(), n.moveTo(y, l), n.lineTo(y, l + o), n.stroke());
|
|
1753
|
+
}
|
|
1754
|
+
n.restore(), this.#Mt(i, s);
|
|
1755
|
+
}
|
|
1756
|
+
/**
|
|
1757
|
+
* Erase ramp over the below-pane overrun strip — the gridline tail stubs
|
|
1758
|
+
* on the main layer, the crosshair's overrun on the overlay. Nothing
|
|
1759
|
+
* erased at the pane floor, total at the strip's bottom edge, so whatever
|
|
1760
|
+
* crosses the floor tapers out short of the time-axis labels.
|
|
1761
|
+
*/
|
|
1762
|
+
#Mt(t, i) {
|
|
1763
|
+
if (this.xAxisHeight === 0) return;
|
|
1764
|
+
const e = Math.round(i), s = Math.round(A * t.verticalPixelRatio);
|
|
1765
|
+
if (s < 1) return;
|
|
1766
|
+
const { context: r } = t;
|
|
1767
|
+
r.save(), r.globalCompositeOperation = "destination-out", r.fillStyle = this.#Yt({ context: r, start: e, height: s }), r.fillRect(0, e, t.bitmapSize.width, s), r.restore();
|
|
1768
|
+
}
|
|
1769
|
+
/**
|
|
1770
|
+
* Crosshair hairlines with the same edge melt the main layer runs: the
|
|
1771
|
+
* pane clip widens by the right-fade intrusion and by a gridline-tail's
|
|
1772
|
+
* overrun below the floor, then the shared erase ramps dissolve the
|
|
1773
|
+
* overhangs — the vertical line tapers toward the time-axis labels in
|
|
1774
|
+
* step with the gridline stubs and the horizontal line rides under the
|
|
1775
|
+
* Y-axis column toward its label instead of stopping dead at the pane
|
|
1776
|
+
* edge. Erases alpha (destination-out), so it must run while the
|
|
1777
|
+
* crosshair is the only content on the layer.
|
|
1778
|
+
*/
|
|
1779
|
+
#Ot(t) {
|
|
1780
|
+
const i = this.#d;
|
|
1781
|
+
if (i === null) return;
|
|
1782
|
+
const { scope: e, chartBitmapWidth: s, chartBitmapHeight: r } = t, { context: n } = e, a = this.#it(e, s), h = this.xAxisHeight > 0 ? Math.round(A * e.verticalPixelRatio) : 0;
|
|
1783
|
+
n.save(), n.beginPath(), n.rect(0, 0, s + a.intrusion, r + h), n.clip(), st({
|
|
1784
|
+
scope: e,
|
|
1785
|
+
bitmapX: i.mediaX * e.horizontalPixelRatio,
|
|
1786
|
+
bitmapY: i.mediaY * e.verticalPixelRatio,
|
|
1787
|
+
theme: this.#n,
|
|
1788
|
+
pane: { width: s, height: r }
|
|
1789
|
+
}), n.restore(), this.#Mt(e, r), this.#At(e, r), this.#Lt(e, s);
|
|
1790
|
+
}
|
|
1791
|
+
/**
|
|
1792
|
+
* Horizontal companions to {@link #applyTopFade}: dissolve content that
|
|
1793
|
+
* rides under the Y-axis column on the right (the pane clip is widened by
|
|
1794
|
+
* the same zone) and, when configured, content leaving through the left
|
|
1795
|
+
* pane edge. Same destination-out erase — background-safe over gradients.
|
|
1796
|
+
* Runs the full bitmap height so the below-pane gridline tail stubs melt
|
|
1797
|
+
* at the edges in step with their lines above.
|
|
1798
|
+
*/
|
|
1799
|
+
#Lt(t, i) {
|
|
1800
|
+
const { context: e } = t, s = t.bitmapSize.height, r = this.#it(t, i);
|
|
1801
|
+
r.width >= 1 && (e.save(), e.globalCompositeOperation = "destination-out", e.fillStyle = this.#Et({ context: e, start: r.start, width: r.width }), e.fillRect(r.start, 0, r.width, s), e.restore());
|
|
1802
|
+
const n = Math.min(Math.round(this.#k.left * t.horizontalPixelRatio), Math.round(i));
|
|
1803
|
+
n >= 1 && (e.save(), e.globalCompositeOperation = "destination-out", e.fillStyle = this.#Xt(e, n), e.fillRect(0, 0, n, s), e.restore());
|
|
1804
|
+
}
|
|
1805
|
+
/**
|
|
1806
|
+
* Bitmap geometry of the right fade zone. The ramp finishes just inside
|
|
1807
|
+
* the Y-axis column — {@link RIGHT_FADE_END_GAP_PX} past the pane edge,
|
|
1808
|
+
* still short of where the right-anchored label glyphs start — so content
|
|
1809
|
+
* is fully erased before it can cross any axis text. Everything wider
|
|
1810
|
+
* spills backward into the pane as the soft lead-in. `width: 0` (axis
|
|
1811
|
+
* hidden, or `right: 0`) means no zone; `intrusion` is what the pane clip
|
|
1812
|
+
* extends by (content past the ramp is total-erased anyway, so the clip
|
|
1813
|
+
* stops with the ramp).
|
|
1814
|
+
*/
|
|
1815
|
+
#it(t, i) {
|
|
1816
|
+
const e = { start: 0, width: 0, intrusion: 0 }, s = Math.max(0, Math.round(t.bitmapSize.width - i));
|
|
1817
|
+
if (s === 0) return e;
|
|
1818
|
+
const r = t.horizontalPixelRatio, n = Math.min(Math.round(xt * r), s), a = this.#k.right === null ? n + Math.round(bt * r) : Math.round(this.#k.right * r);
|
|
1819
|
+
if (a < 1) return e;
|
|
1820
|
+
const h = i + n, l = Math.min(a, h);
|
|
1821
|
+
return { start: h - l, width: l, intrusion: n };
|
|
1822
|
+
}
|
|
1823
|
+
/** Memoized mask gradient — rebuilt only when the bitmap zone height
|
|
1824
|
+
* changes (resize, DPR change, `setFade`). Ease-out ramp (`1 - t²`):
|
|
1825
|
+
* near-total erasure across the upper half keeps a floating header
|
|
1826
|
+
* legible, then a soft tail into full opacity at the zone's bottom. */
|
|
1827
|
+
#Nt(t, i) {
|
|
1828
|
+
const e = this.#w.top;
|
|
1829
|
+
if (e !== null && e.height === i) return e.gradient;
|
|
1830
|
+
const s = t.createLinearGradient(0, 0, 0, i);
|
|
1831
|
+
for (let r = 0; r <= 4; r++) {
|
|
1832
|
+
const n = r / 4;
|
|
1833
|
+
s.addColorStop(n, `rgba(0, 0, 0, ${1 - n * n})`);
|
|
1834
|
+
}
|
|
1835
|
+
return this.#w.top = { height: i, gradient: s }, s;
|
|
1836
|
+
}
|
|
1837
|
+
/** Right-zone mask — erase grows from 0 at the ramp's inner start to
|
|
1838
|
+
* total at its outer end on an ease-in (`s³`): zero slope *and* zero
|
|
1839
|
+
* curvature at the entry, so the in-pane lead-in barely dims the newest
|
|
1840
|
+
* candles at rest and the aggressive erasing all happens deep under the
|
|
1841
|
+
* axis column. Nine stops keep the piecewise interpolation smooth across
|
|
1842
|
+
* the zone. */
|
|
1843
|
+
#Et(t) {
|
|
1844
|
+
const i = this.#w.right;
|
|
1845
|
+
if (i !== null && i.start === t.start && i.width === t.width) return i.gradient;
|
|
1846
|
+
const e = t.context.createLinearGradient(t.start, 0, t.start + t.width, 0);
|
|
1847
|
+
for (let s = 0; s <= 8; s++) {
|
|
1848
|
+
const r = s / 8;
|
|
1849
|
+
e.addColorStop(r, `rgba(0, 0, 0, ${r * r * r})`);
|
|
1850
|
+
}
|
|
1851
|
+
return this.#w.right = { start: t.start, width: t.width, gradient: e }, e;
|
|
1852
|
+
}
|
|
1853
|
+
/** Erase ramp for the gridline tail stubs — nothing erased at the pane
|
|
1854
|
+
* floor, total at the stub tip, ease-in (`t²`) so a stub holds strength
|
|
1855
|
+
* briefly before melting away short of the label glyphs. */
|
|
1856
|
+
#Yt(t) {
|
|
1857
|
+
const i = this.#w.tail;
|
|
1858
|
+
if (i !== null && i.start === t.start && i.height === t.height) return i.gradient;
|
|
1859
|
+
const e = t.context.createLinearGradient(0, t.start, 0, t.start + t.height);
|
|
1860
|
+
for (let s = 0; s <= 4; s++) {
|
|
1861
|
+
const r = s / 4;
|
|
1862
|
+
e.addColorStop(r, `rgba(0, 0, 0, ${r * r})`);
|
|
1863
|
+
}
|
|
1864
|
+
return this.#w.tail = { start: t.start, height: t.height, gradient: e }, e;
|
|
1865
|
+
}
|
|
1866
|
+
/** Left-zone mask — mirror of the top ramp: total erase at the canvas
|
|
1867
|
+
* edge, fully opaque at the zone's inner boundary. */
|
|
1868
|
+
#Xt(t, i) {
|
|
1869
|
+
const e = this.#w.left;
|
|
1870
|
+
if (e !== null && e.width === i) return e.gradient;
|
|
1871
|
+
const s = t.createLinearGradient(0, 0, i, 0);
|
|
1872
|
+
for (let r = 0; r <= 4; r++) {
|
|
1873
|
+
const n = r / 4;
|
|
1874
|
+
s.addColorStop(n, `rgba(0, 0, 0, ${1 - n * n})`);
|
|
1875
|
+
}
|
|
1876
|
+
return this.#w.left = { width: i, gradient: s }, s;
|
|
1877
|
+
}
|
|
1675
1878
|
/**
|
|
1676
1879
|
* Paint a full frame: the main layer, then the overlay synchronously on
|
|
1677
1880
|
* top so both land in the same browser paint. The overlay scheduler's
|
|
@@ -1681,13 +1884,13 @@ class qt extends at {
|
|
|
1681
1884
|
* `markDirty` calls made *during* `renderOverlay` (pulse, edge spinner)
|
|
1682
1885
|
* land after the consume and re-arm cleanly for the next frame.
|
|
1683
1886
|
*/
|
|
1684
|
-
#
|
|
1685
|
-
this.#
|
|
1887
|
+
#et(t) {
|
|
1888
|
+
this.#st("main", t, () => this.renderMain(t)), this.#h.consume(), this.#st("overlay", t, () => this.renderOverlay(t));
|
|
1686
1889
|
}
|
|
1687
1890
|
/** Run a layer's paint, booking duration + draw calls to the perf monitor
|
|
1688
1891
|
* when one is attached — keeps overlay time out of the `main` samples. */
|
|
1689
|
-
#
|
|
1690
|
-
const s = this.#
|
|
1892
|
+
#st(t, i, e) {
|
|
1893
|
+
const s = this.#S;
|
|
1691
1894
|
if (!s) {
|
|
1692
1895
|
e();
|
|
1693
1896
|
return;
|
|
@@ -1708,21 +1911,18 @@ class qt extends at {
|
|
|
1708
1911
|
}
|
|
1709
1912
|
this.#m.some((o) => o.pulse) && (e = !0);
|
|
1710
1913
|
const s = {
|
|
1711
|
-
left: this.#
|
|
1712
|
-
right: this.#
|
|
1914
|
+
left: this.#I.left && this.#x.left === "loading" ? "idle" : this.#x.left,
|
|
1915
|
+
right: this.#I.right && this.#x.right === "loading" ? "idle" : this.#x.right
|
|
1713
1916
|
}, r = {
|
|
1714
|
-
left: this.#
|
|
1715
|
-
right: this.#
|
|
1716
|
-
}, n = r.left !== null || r.right !== null,
|
|
1717
|
-
if (!(!l && !this.#
|
|
1718
|
-
if (this.#
|
|
1917
|
+
left: this.#vt("left"),
|
|
1918
|
+
right: this.#vt("right")
|
|
1919
|
+
}, n = r.left !== null || r.right !== null, a = s.left === "loading" || s.right === "loading" || n, h = s.left !== "idle" || s.right !== "idle" || n, l = !!this.#d || e || h || this.#m.length > 0 || this.#y.length > 0;
|
|
1920
|
+
if (!(!l && !this.#at)) {
|
|
1921
|
+
if (this.#at = l, this.#a.useOverlayLayer((o) => {
|
|
1719
1922
|
if (!l) return;
|
|
1720
|
-
const u = (i.media.width - this.yAxisWidth) * i.horizontalPixelRatio,
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
tt(o, m, g, this.#n);
|
|
1724
|
-
}
|
|
1725
|
-
const c = this.#c, p = { top: c.top, bottom: c.bottom }, v = this.#s.getAnimationState();
|
|
1923
|
+
const u = (i.media.width - this.yAxisWidth) * i.horizontalPixelRatio, c = (i.media.height - this.xAxisHeight) * i.verticalPixelRatio;
|
|
1924
|
+
this.#d && this.#yt() && this.#Ot({ scope: o, chartBitmapWidth: u, chartBitmapHeight: c }), o.context.save(), o.context.beginPath(), o.context.rect(0, 0, u, c), o.context.clip();
|
|
1925
|
+
const d = this.#c, p = { top: d.top, bottom: d.bottom }, f = this.#s.getAnimationState();
|
|
1726
1926
|
for (const m of this.#t)
|
|
1727
1927
|
m.visible && m.renderer.drawOverlay?.({
|
|
1728
1928
|
scope: o,
|
|
@@ -1732,10 +1932,10 @@ class qt extends at {
|
|
|
1732
1932
|
dataInterval: this.#r,
|
|
1733
1933
|
padding: p,
|
|
1734
1934
|
crosshair: this.#d,
|
|
1735
|
-
state:
|
|
1935
|
+
state: f,
|
|
1736
1936
|
seriesId: m.id
|
|
1737
1937
|
});
|
|
1738
|
-
this.#
|
|
1938
|
+
this.#Pt(o, u, c), this.#Ct(o, u, c), h && Z({
|
|
1739
1939
|
scope: o,
|
|
1740
1940
|
chartMediaWidth: i.media.width - this.yAxisWidth,
|
|
1741
1941
|
chartMediaHeight: i.media.height - this.xAxisHeight,
|
|
@@ -1743,35 +1943,35 @@ class qt extends at {
|
|
|
1743
1943
|
theme: this.#n,
|
|
1744
1944
|
edgeStates: s,
|
|
1745
1945
|
edgeExitFades: r,
|
|
1746
|
-
edgeIndicatorFns: this.#
|
|
1747
|
-
resolveBoundary: (m) =>
|
|
1748
|
-
resolveEdgeAnchor: (m,
|
|
1749
|
-
resolveEdgeBarSpacing: () => this.#
|
|
1750
|
-
reportPlaceholders: (m,
|
|
1751
|
-
this.#
|
|
1946
|
+
edgeIndicatorFns: this.#lt,
|
|
1947
|
+
resolveBoundary: (m) => $(m, this.#ht[m], this.getDataBounds()),
|
|
1948
|
+
resolveEdgeAnchor: (m, y) => this.#Ht(m, y),
|
|
1949
|
+
resolveEdgeBarSpacing: () => this.#zt(),
|
|
1950
|
+
reportPlaceholders: (m, y) => {
|
|
1951
|
+
this.#Z[m] = { bars: y, at: performance.now() };
|
|
1752
1952
|
}
|
|
1753
1953
|
}), o.context.restore();
|
|
1754
1954
|
}), e) {
|
|
1755
1955
|
const { from: o, to: u } = this.timeScale.getRange();
|
|
1756
|
-
let
|
|
1757
|
-
for (const
|
|
1758
|
-
if (!(!
|
|
1759
|
-
|
|
1956
|
+
let c = !1;
|
|
1957
|
+
for (const d of this.#t)
|
|
1958
|
+
if (!(!d.visible || !d.renderer.overlayNeedsAnimation) && (d.renderer.hasOverlayContentInRange?.(o, u) ?? !0)) {
|
|
1959
|
+
c = !0;
|
|
1760
1960
|
break;
|
|
1761
1961
|
}
|
|
1762
|
-
if (!
|
|
1763
|
-
for (const
|
|
1764
|
-
if (
|
|
1765
|
-
|
|
1962
|
+
if (!c) {
|
|
1963
|
+
for (const d of this.#m)
|
|
1964
|
+
if (d.pulse && d.time >= o && d.time <= u) {
|
|
1965
|
+
c = !0;
|
|
1766
1966
|
break;
|
|
1767
1967
|
}
|
|
1768
1968
|
}
|
|
1769
|
-
|
|
1969
|
+
c && this.#h.markDirty();
|
|
1770
1970
|
}
|
|
1771
|
-
|
|
1971
|
+
a && this.#h.markDirty();
|
|
1772
1972
|
}
|
|
1773
1973
|
}
|
|
1774
1974
|
}
|
|
1775
1975
|
export {
|
|
1776
|
-
|
|
1976
|
+
ti as ChartInstance
|
|
1777
1977
|
};
|