@wick-charts/vue 0.6.3 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/dist/BandSeries.vue.js +52 -0
- package/dist/BandSeries.vue2.js +4 -0
- package/dist/BarSeries.vue.js +15 -13
- package/dist/CandlestickSeries.vue.js +31 -29
- package/dist/ChartContainer.vue.js +75 -68
- package/dist/LineSeries.vue.js +19 -17
- package/dist/composables.js +46 -36
- package/dist/core/dist/animation/config.js +92 -75
- package/dist/core/dist/animation/viewport-engine.js +155 -96
- package/dist/core/dist/axis/dom-labels.js +56 -51
- package/dist/core/dist/chart/badge-animator.js +30 -22
- package/dist/core/dist/chart/edge-indicators.js +22 -21
- package/dist/core/dist/chart/last-value.js +33 -26
- package/dist/core/dist/chart/scale-guards.js +23 -5
- package/dist/core/dist/chart/x-guards.js +37 -0
- package/dist/core/dist/chart.js +1286 -842
- package/dist/core/dist/data/decimation.js +58 -39
- package/dist/core/dist/data/sync.js +37 -31
- package/dist/core/dist/navigator/controller.js +41 -41
- package/dist/core/dist/perf/perf-hud.js +3 -3
- package/dist/core/dist/scales/tick-tracker.js +2 -2
- package/dist/core/dist/scales/x-scale.js +167 -53
- package/dist/core/dist/scales/y-scale.js +36 -34
- package/dist/core/dist/series/band.js +723 -0
- package/dist/core/dist/series/bar.js +297 -291
- package/dist/core/dist/series/base-multi-layer.js +283 -237
- package/dist/core/dist/series/candlestick.js +32 -31
- package/dist/core/dist/series/heatmap.js +5 -5
- package/dist/core/dist/series/line-intro.js +47 -37
- package/dist/core/dist/series/line.js +489 -448
- package/dist/core/dist/series/painters/builtins.js +4 -4
- package/dist/core/dist/series/painters/canvas-path.js +64 -52
- package/dist/core/dist/series/register-builtins.js +9 -8
- package/dist/core/dist/snapshots.js +59 -46
- package/dist/core/dist/utils/point.js +10 -0
- package/dist/index.d.ts +552 -129
- package/dist/index.js +195 -191
- package/dist/ui/Crosshair.vue.js +51 -27
- package/dist/ui/InfoBar.vue.js +98 -79
- package/dist/ui/Marker.vue.js +14 -11
- package/dist/ui/Navigator.vue.js +23 -23
- package/dist/ui/ReferenceLine.vue.js +19 -16
- package/dist/ui/Tooltip.vue.js +116 -84
- package/dist/ui/XAxis.vue.js +48 -0
- package/dist/ui/XAxis.vue2.js +4 -0
- package/dist/ui/YAxis.vue.js +44 -28
- package/dist/ui/YLabel.vue.js +82 -85
- package/package.json +2 -2
- package/src/BandSeries.vue +92 -0
- package/src/BarSeries.vue +13 -6
- package/src/CandlestickSeries.vue +9 -5
- package/src/ChartContainer.vue +20 -0
- package/src/LineSeries.vue +13 -6
- package/src/composables.ts +23 -4
- package/src/index.ts +16 -1
- package/src/ui/Crosshair.vue +41 -1
- package/src/ui/InfoBar.vue +22 -2
- package/src/ui/Marker.vue +5 -1
- package/src/ui/Navigator.vue +2 -1
- package/src/ui/ReferenceLine.vue +5 -1
- package/src/ui/Tooltip.vue +43 -6
- package/src/ui/{TimeAxis.vue → XAxis.vue} +18 -5
- package/src/ui/YAxis.vue +66 -12
- package/src/ui/YLabel.vue +37 -18
- package/dist/ui/TimeAxis.vue.js +0 -44
- package/dist/ui/TimeAxis.vue2.js +0 -4
package/dist/core/dist/chart.js
CHANGED
|
@@ -1,64 +1,100 @@
|
|
|
1
|
-
import { Animator as
|
|
2
|
-
import { AnimationConfig as
|
|
1
|
+
import { Animator as nt } from "./animation/animator.js";
|
|
2
|
+
import { AnimationConfig as at, DEFAULT_LINE_PULSE as ot } from "./animation/config.js";
|
|
3
3
|
import { easeOutCubic as W } from "./animation/easing.js";
|
|
4
|
-
import { prefersReducedMotion as
|
|
5
|
-
import { createViewportEngine as
|
|
6
|
-
import { CanvasManager as
|
|
7
|
-
import { resolveEdgeAnchorValue as
|
|
8
|
-
import { computeFitToData as
|
|
9
|
-
import { getLastValue as
|
|
10
|
-
import { resolveFade as
|
|
11
|
-
import { computePan as
|
|
12
|
-
import { warnUnplottableBounds as
|
|
13
|
-
import { StreamingCadence as
|
|
14
|
-
import { computeStreamingTarget as
|
|
15
|
-
import { resolvePaddingTime as
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
4
|
+
import { prefersReducedMotion as ht } from "./animation/reduced-motion.js";
|
|
5
|
+
import { createViewportEngine as lt } from "./animation/viewport-engine.js";
|
|
6
|
+
import { CanvasManager as ct } from "./canvas-manager.js";
|
|
7
|
+
import { resolveEdgeAnchorValue as dt, drawEdgeIndicators as ut, resolveEdgeBoundary as mt } from "./chart/edge-indicators.js";
|
|
8
|
+
import { computeFitToData as ft } from "./chart/fit-to-data.js";
|
|
9
|
+
import { getLastValue as gt, getPreviousClose as pt, getStackedLastValue as yt } from "./chart/last-value.js";
|
|
10
|
+
import { resolveFade as Y, resolveMaxVisibleBars as xt, resolvePadding as E, resolvePerfOptions as vt, isSameHorizontalPadding as B } from "./chart/options.js";
|
|
11
|
+
import { computePan as St, computeZoom as bt } from "./chart/pan-zoom-math.js";
|
|
12
|
+
import { warnUnplottableBounds as C, assertStackingFitsScale as L, warnScaleSideTaken as kt, warnUnknownScale as wt, warnStackingOffScale as Tt, warnUnplottableValues as At } from "./chart/scale-guards.js";
|
|
13
|
+
import { StreamingCadence as Mt } from "./chart/streaming-cadence.js";
|
|
14
|
+
import { computeStreamingTarget as Pt } from "./chart/streaming-target.js";
|
|
15
|
+
import { resolvePaddingTime as N } from "./chart/viewport-padding.js";
|
|
16
|
+
import { assertSeriesFitsXAxis as Ct, warnXMisfit as Lt, warnXTimeMixed as Dt, warnTimeOnlyFeature as Rt, warnXTypeChange as It } from "./chart/x-guards.js";
|
|
17
|
+
import { fromScaleSpace as Ht, computeTargetYRange as Vt, resolveYTarget as zt } from "./chart/y-target.js";
|
|
18
|
+
import { renderCrosshair as Ft } from "./components/crosshair.js";
|
|
19
|
+
import { renderGrid as X } from "./components/grid.js";
|
|
20
|
+
import { renderMarker as Ot } from "./components/marker.js";
|
|
21
|
+
import { renderReferenceLine as $t } from "./components/reference-line.js";
|
|
22
|
+
import { resolveTimeRegionColors as Wt, renderTimeRegion as Yt } from "./components/time-region.js";
|
|
23
|
+
import { EventEmitter as Et } from "./events.js";
|
|
24
|
+
import { InteractionHandler as Bt } from "./interactions/handler.js";
|
|
25
|
+
import { RenderScheduler as Z } from "./render-scheduler.js";
|
|
26
|
+
import { XScale as Nt } from "./scales/x-scale.js";
|
|
27
|
+
import { YScale as U, isPlottableOn as _ } from "./scales/y-scale.js";
|
|
28
|
+
import { resolveSeriesDefinition as Xt } from "./series/definition.js";
|
|
29
|
+
import { isTimeSeriesRenderer as v } from "./series/types.js";
|
|
30
|
+
import { catppuccin as Zt } from "./theme/themes/catppuccin.js";
|
|
31
|
+
import { deepEqual as Ut } from "./utils/deep-equal.js";
|
|
32
|
+
import { clamp as _t } from "./utils/math.js";
|
|
33
|
+
import { crispLineWidth as Gt, crispCenterOffset as Kt } from "./utils/pixel-grid.js";
|
|
34
|
+
import { pointValue as G, isBandPoint as jt } from "./utils/point.js";
|
|
35
|
+
import { normalizeTime as k, detectInterval as qt } from "./utils/time.js";
|
|
36
|
+
const Jt = 200, Qt = 1500, te = 48, K = 12, D = 9, ee = ["left", "right"], ie = { entries: [], isAnimating: !1 }, se = [], re = ["left"], ne = ["right"], ae = 55;
|
|
37
|
+
function j(p, t) {
|
|
38
|
+
return {
|
|
39
|
+
yScale: p,
|
|
40
|
+
bounds: { min: t?.min, max: t?.max },
|
|
41
|
+
range: { min: 0, max: 0 },
|
|
42
|
+
prevMin: Number.NaN,
|
|
43
|
+
prevMax: Number.NaN
|
|
44
|
+
};
|
|
37
45
|
}
|
|
38
|
-
function
|
|
39
|
-
return
|
|
46
|
+
function q(p) {
|
|
47
|
+
return p === "left" ? "right" : "left";
|
|
40
48
|
}
|
|
41
|
-
|
|
49
|
+
function J(p, t) {
|
|
50
|
+
return p === void 0 || p === "auto" ? !1 : !(typeof p == "number" && !t.isPlottable(p));
|
|
51
|
+
}
|
|
52
|
+
let oe = 0, he = 0, le = 0, ce = 0;
|
|
53
|
+
function Q(p, t) {
|
|
54
|
+
return p === t ? !0 : !p || !t || p.length !== t.length ? !1 : p.every((e, i) => e === t[i]);
|
|
55
|
+
}
|
|
56
|
+
function tt(p) {
|
|
57
|
+
return v(p) ? p.stacking : void 0;
|
|
58
|
+
}
|
|
59
|
+
function de(p, t) {
|
|
60
|
+
return p === t ? !0 : !p || !t || p.length !== t.length ? !1 : p.every((e, i) => e === t[i]);
|
|
61
|
+
}
|
|
62
|
+
class _e extends Et {
|
|
42
63
|
/** Canvas lifecycle and DPR-aware sizing. */
|
|
43
|
-
#
|
|
64
|
+
#l;
|
|
44
65
|
/** Schedules main-layer redraws (background, grid, series). */
|
|
45
66
|
#i;
|
|
46
67
|
/** Schedules overlay redraws (crosshair). */
|
|
47
|
-
#
|
|
68
|
+
#c;
|
|
48
69
|
/** Maps time values to horizontal pixel coordinates. */
|
|
49
70
|
timeScale;
|
|
50
|
-
/** Maps price/value to vertical pixel coordinates. */
|
|
71
|
+
/** Maps price/value to vertical pixel coordinates — the default Y scale. */
|
|
51
72
|
yScale;
|
|
73
|
+
/** Default-scale Y state; `yScale` is its scale. */
|
|
74
|
+
#P;
|
|
75
|
+
/** Named Y scales from `options.scales`, in declaration order. */
|
|
76
|
+
#a = /* @__PURE__ */ new Map();
|
|
77
|
+
/** Copy of the `scales` option as last applied — `setScales` skips a deep-equal replay,
|
|
78
|
+
* and the copy keeps an in-place edit of the caller's object from comparing equal. */
|
|
79
|
+
#At = {};
|
|
80
|
+
/** Set when a named scale's range moved, so the crosshair re-emits for its axis label. */
|
|
81
|
+
#nt = !1;
|
|
82
|
+
/** Keys of dev warnings already printed, so each fires once. */
|
|
83
|
+
#Mt = /* @__PURE__ */ new Set();
|
|
84
|
+
/** What the X axis maps — read from `axis.x.type` once, at construction. */
|
|
85
|
+
#r;
|
|
86
|
+
/** Category axis: name → band index, in first-seen order across the series. */
|
|
87
|
+
#Y = /* @__PURE__ */ new Map();
|
|
52
88
|
/** Zoom, pan, crosshair — null when interactive=false. */
|
|
53
|
-
#
|
|
89
|
+
#V;
|
|
54
90
|
/** All registered series (candlestick, line, bar, pie). */
|
|
55
91
|
#t = [];
|
|
56
92
|
/** Point annotations (event markers) drawn on the overlay layer, kept out of the series model. */
|
|
57
|
-
#
|
|
93
|
+
#y = [];
|
|
58
94
|
/** Time-region bands drawn on the main layer behind the series, kept out of the series model. */
|
|
59
|
-
#
|
|
95
|
+
#T = [];
|
|
60
96
|
/** Reference lines (horizontal threshold / vertical event) drawn on the overlay layer, kept out of the series model. */
|
|
61
|
-
#
|
|
97
|
+
#b = [];
|
|
62
98
|
/**
|
|
63
99
|
* Overlay-intro latch. Annotations ride the *initial* reveal, then stay put.
|
|
64
100
|
* `#overlayIntroSeen` flips once we've actually watched an intro in progress
|
|
@@ -67,31 +103,31 @@ class Ai extends At {
|
|
|
67
103
|
* later — which re-arms `getIntroFront()`'s global min — can't blink settled
|
|
68
104
|
* markers / reference lines back out.
|
|
69
105
|
*/
|
|
70
|
-
#
|
|
71
|
-
#
|
|
106
|
+
#Pt = !1;
|
|
107
|
+
#Ct = !1;
|
|
72
108
|
/** Active visual theme (colors, fonts, grid style). */
|
|
73
109
|
#n;
|
|
74
110
|
/** Whether to render the background grid. */
|
|
75
|
-
#
|
|
111
|
+
#G;
|
|
76
112
|
/**
|
|
77
113
|
* Gridline layer opacity, multiplied into every per-tick fade: `0 → 1` on
|
|
78
114
|
* the opening reveal, `1 → 0` when {@link setGrid} hides the grid. The
|
|
79
115
|
* per-tick fades stay snapped during mount (see the tracker's arming), so
|
|
80
116
|
* this is what keeps `fitToData` tick churn from flickering the reveal.
|
|
81
117
|
*/
|
|
82
|
-
#
|
|
118
|
+
#E;
|
|
83
119
|
/** Resolved edge-fade zones — a `null` side is auto: the axis-column ramp on the Y-axis side, off on the other. */
|
|
84
|
-
#
|
|
120
|
+
#C;
|
|
85
121
|
/** See {@link headerHeight}. */
|
|
86
|
-
#
|
|
122
|
+
#at = 0;
|
|
87
123
|
/** Memoized destination-out mask gradients, one per edge, keyed by the
|
|
88
124
|
* zone's bitmap geometry so a resize / DPR change / `setFade` rebuilds
|
|
89
125
|
* them and steady frames reuse them. */
|
|
90
|
-
#
|
|
126
|
+
#k = { top: null, axis: { left: null, right: null }, edge: { left: null, right: null }, tail: null, overhang: null };
|
|
91
127
|
/** Detected time interval between data points (milliseconds). */
|
|
92
|
-
#
|
|
128
|
+
#o = 6e4;
|
|
93
129
|
/** Current crosshair position, null when cursor is outside the chart. */
|
|
94
|
-
#
|
|
130
|
+
#f = null;
|
|
95
131
|
/**
|
|
96
132
|
* What the crosshair is anchored to, deciding which half of the position
|
|
97
133
|
* survives a viewport move (see {@link #syncCrosshairToViewport}):
|
|
@@ -99,45 +135,43 @@ class Ai extends At {
|
|
|
99
135
|
* `time`/`y`; `'time'` (programmatic {@link setCrosshair}) keeps
|
|
100
136
|
* `time`/`y` and re-projects the pixel position.
|
|
101
137
|
*/
|
|
102
|
-
#
|
|
138
|
+
#ot = "pointer";
|
|
103
139
|
/** Whether the last overlay pass painted anything — gates the full-bitmap
|
|
104
140
|
* clear so an empty overlay isn't cleared again every main frame. */
|
|
105
|
-
#
|
|
106
|
-
/** User-specified Y-axis bounds (auto, fixed, percentage). */
|
|
107
|
-
#p = {};
|
|
141
|
+
#Lt = !1;
|
|
108
142
|
/** Cached series ID list — invalidated on add/remove. */
|
|
109
|
-
#
|
|
143
|
+
#z = null;
|
|
110
144
|
/** Axis visibility and sizing configuration. */
|
|
111
|
-
#
|
|
145
|
+
#v = {};
|
|
112
146
|
/** Host-declared state per edge — drives the edge-indicator overlay. */
|
|
113
|
-
#
|
|
147
|
+
#A = { left: "idle", right: "idle" };
|
|
114
148
|
/** Wall-clock start of a `loading` exit fade per side — set when the host
|
|
115
149
|
* drops `loading` back to `idle`/`has-more`, so the indicator fades out
|
|
116
150
|
* over {@link EDGE_EXIT_FADE_MS} instead of vanishing the same frame the
|
|
117
151
|
* fetched data lands. `null` = no fade in flight. */
|
|
118
|
-
#
|
|
152
|
+
#K = { left: null, right: null };
|
|
119
153
|
/** Cached boundary timestamps from the last `edgeReached` emission, by side. */
|
|
120
|
-
#
|
|
154
|
+
#Dt = { left: null, right: null };
|
|
121
155
|
/** Latest placeholder shapes a `loading` indicator reported per side —
|
|
122
156
|
* the morph hand-off candidate consumed by {@link prependData}. */
|
|
123
|
-
#
|
|
157
|
+
#ht = { left: null, right: null };
|
|
124
158
|
/** True after a prepend consumed a side's placeholder snapshot: the series'
|
|
125
159
|
* reveal owns those pixels now, so the still-`loading` indicator is hidden
|
|
126
160
|
* and the upcoming `loading → idle` transition skips its exit fade. Reset
|
|
127
161
|
* by the next {@link setEdgeState} call. */
|
|
128
|
-
#
|
|
162
|
+
#B = { left: !1, right: !1 };
|
|
129
163
|
/** Host-supplied callback fired when the user releases a pan/zoom past a data edge. */
|
|
130
|
-
#
|
|
164
|
+
#Rt;
|
|
131
165
|
/** Per-side custom painter for the edge `loading` spinner. `null` falls back to the built-in dots. */
|
|
132
|
-
#
|
|
166
|
+
#It = { left: null, right: null };
|
|
133
167
|
/** Nesting depth for batch updates. Suppresses recomputes while > 0. */
|
|
134
|
-
#
|
|
168
|
+
#R = 0;
|
|
135
169
|
/** True when batched operations include data changes (triggers full onDataChanged on end). */
|
|
136
|
-
#
|
|
170
|
+
#lt = !1;
|
|
137
171
|
/** True when batched operations include visibility changes (triggers Y-range + redraw on end). */
|
|
138
|
-
#
|
|
172
|
+
#N = !1;
|
|
139
173
|
/** True when batched operations bumped overlay version (emits a single overlayChange on flush). */
|
|
140
|
-
#
|
|
174
|
+
#ct = !1;
|
|
141
175
|
/**
|
|
142
176
|
* Monotonic counter bumped on any mutation that affects overlay output —
|
|
143
177
|
* data, visibility, series options, theme. Used by snapshot helpers in
|
|
@@ -147,36 +181,59 @@ class Ai extends At {
|
|
|
147
181
|
*
|
|
148
182
|
* @internal
|
|
149
183
|
*/
|
|
150
|
-
#
|
|
151
|
-
/** CSS px of the Y-axis column. The default collapses to `0` on a spatial-only chart. */
|
|
184
|
+
#Ht = 0;
|
|
185
|
+
/** CSS px of the default Y-axis column. The default collapses to `0` on a spatial-only chart. */
|
|
152
186
|
get yAxisWidth() {
|
|
153
|
-
|
|
154
|
-
return t?.visible === !1 ? 0 : t?.width !== void 0 ? t.width : this.#X() ? 0 : 55;
|
|
187
|
+
return this.#j(this.#v.y);
|
|
155
188
|
}
|
|
156
|
-
/** Side of the plot area the Y-axis column sits on. */
|
|
189
|
+
/** Side of the plot area the default Y-axis column sits on. */
|
|
157
190
|
get yAxisPosition() {
|
|
158
|
-
return this.#
|
|
191
|
+
return this.#v.y?.position ?? "right";
|
|
159
192
|
}
|
|
160
193
|
/**
|
|
161
|
-
* Left edge of the plot area in container CSS px — the Y-axis
|
|
162
|
-
*
|
|
163
|
-
*
|
|
194
|
+
* Left edge of the plot area in container CSS px — the width of the Y-axis
|
|
195
|
+
* column on the left, `0` without one. Scales map into the plot area, so
|
|
196
|
+
* add this to `timeScale.timeToX()` to get a container coordinate.
|
|
164
197
|
*/
|
|
165
198
|
get plotLeft() {
|
|
166
|
-
return this.
|
|
199
|
+
return this.getAxisColumnWidth("left");
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* CSS px of the Y-axis column on `side` — the default axis's or the named
|
|
203
|
+
* scale's that owns that side — `0` when no column sits there.
|
|
204
|
+
*/
|
|
205
|
+
getAxisColumnWidth(t) {
|
|
206
|
+
if (t === this.yAxisPosition && this.yAxisWidth > 0) return this.yAxisWidth;
|
|
207
|
+
for (const e of this.#a.values())
|
|
208
|
+
if (e.column === t) return this.#j(e.config);
|
|
209
|
+
return 0;
|
|
210
|
+
}
|
|
211
|
+
/** The named scale whose axis column sits on `side`, `null` when the default axis's does or none. */
|
|
212
|
+
getAxisColumnScale(t) {
|
|
213
|
+
for (const [e, i] of this.#a)
|
|
214
|
+
if (i.column === t) return e;
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
/** Both Y-axis columns together — the plot area is the canvas width minus this. */
|
|
218
|
+
#w() {
|
|
219
|
+
return this.getAxisColumnWidth("left") + this.getAxisColumnWidth("right");
|
|
220
|
+
}
|
|
221
|
+
/** Column width a Y-axis config asks for; `0` when hidden, and by default on a spatial-only chart. */
|
|
222
|
+
#j(t) {
|
|
223
|
+
return t?.visible === !1 ? 0 : t?.width !== void 0 ? t.width : this.#Q() ? 0 : ae;
|
|
167
224
|
}
|
|
168
225
|
/** CSS px height of the floating Title / InfoBar header, `0` without one. A left
|
|
169
226
|
* axis shares its column with it, so tick labels fade out underneath. */
|
|
170
227
|
get headerHeight() {
|
|
171
|
-
return this.#
|
|
228
|
+
return this.#at;
|
|
172
229
|
}
|
|
173
230
|
/** CSS px of the time-axis row. The default collapses to `0` on a spatial-only chart. */
|
|
174
231
|
get xAxisHeight() {
|
|
175
|
-
const t = this.#
|
|
176
|
-
return t?.visible === !1 ? 0 : t?.height !== void 0 ? t.height : this.#
|
|
232
|
+
const t = this.#v.x;
|
|
233
|
+
return t?.visible === !1 ? 0 : t?.height !== void 0 ? t.height : this.#Q() ? 0 : 30;
|
|
177
234
|
}
|
|
178
235
|
/** Resolved animation config derived from `options.animations` at construction. */
|
|
179
|
-
#
|
|
236
|
+
#g;
|
|
180
237
|
/**
|
|
181
238
|
* Single source of truth for the chart's X / Y viewport animation.
|
|
182
239
|
* Push-model API: chart emits intent signals (`onPointAppended`,
|
|
@@ -185,13 +242,13 @@ class Ai extends At {
|
|
|
185
242
|
* matching targets back through `computeXTarget` / `computeYTarget`
|
|
186
243
|
* closures supplied at construction.
|
|
187
244
|
*/
|
|
188
|
-
#
|
|
245
|
+
#e;
|
|
189
246
|
/** EMA-smoothed real-wall-clock cadence between streaming appends. The
|
|
190
247
|
* X spring's `setSettleMs` is fed from this so the settle time stays just
|
|
191
248
|
* longer than the producer's tick interval — the spring never quite
|
|
192
249
|
* settles between ticks, so velocity carries continuously and the slide
|
|
193
250
|
* doesn't pulse. */
|
|
194
|
-
#
|
|
251
|
+
#F;
|
|
195
252
|
/**
|
|
196
253
|
* Mirrors `#dataReplaceSnapPending` for the engine signal that follows
|
|
197
254
|
* inside the same `onDataChanged` call. `#dataReplaceSnapPending` is
|
|
@@ -201,31 +258,31 @@ class Ai extends At {
|
|
|
201
258
|
* field carries the decision across that gap. The closure also reads
|
|
202
259
|
* `#dataStart` / `#dataEnd` directly for the timestamps.
|
|
203
260
|
*/
|
|
204
|
-
#
|
|
261
|
+
#Vt = !1;
|
|
205
262
|
/** Earliest data timestamp registered across all series, `null` before any data has arrived. */
|
|
206
|
-
#
|
|
263
|
+
#S = null;
|
|
207
264
|
/** Latest data timestamp registered across all series, `null` before any data has arrived. */
|
|
208
|
-
#
|
|
265
|
+
#s = null;
|
|
209
266
|
/**
|
|
210
267
|
* Previous {@link #dataEnd} value. Streaming-target math reads it to
|
|
211
268
|
* preserve any pan offset across ticks: a user who panned a few bars
|
|
212
269
|
* left of the tail keeps that offset as new bars arrive, instead of
|
|
213
270
|
* snapping back to the natural-pin position every frame.
|
|
214
271
|
*/
|
|
215
|
-
#
|
|
272
|
+
#q = null;
|
|
216
273
|
/**
|
|
217
274
|
* Warm-up window flag — `setVisibleRange({ from, bars })` arms it and
|
|
218
275
|
* the streaming-target math suppresses pan ticks while the data hasn't
|
|
219
276
|
* reached the right edge yet. Cleared by user interaction or once the
|
|
220
277
|
* data fills the window.
|
|
221
278
|
*/
|
|
222
|
-
#
|
|
279
|
+
#L = !1;
|
|
223
280
|
/**
|
|
224
281
|
* Cap for fit-to-data. When the data span exceeds this, the fit anchors
|
|
225
282
|
* the right edge and trims the left. Resolved once at construction from
|
|
226
283
|
* `options.viewport.maxVisibleBars`.
|
|
227
284
|
*/
|
|
228
|
-
#
|
|
285
|
+
#zt;
|
|
229
286
|
/**
|
|
230
287
|
* Committed logical X range — the "where X should be" target. Engine
|
|
231
288
|
* eases its visual toward this. Pan / zoom / setRange / fitContent /
|
|
@@ -233,69 +290,75 @@ class Ai extends At {
|
|
|
233
290
|
* as "uninitialized" by `#computeXTarget` until first paint.
|
|
234
291
|
*/
|
|
235
292
|
#h = { from: 0, to: 0 };
|
|
236
|
-
/** Padded Y range — engine's raw Y target plus pixel padding from `#padding`. */
|
|
237
|
-
#B = { min: 0, max: 0 };
|
|
238
293
|
/**
|
|
239
294
|
* Auto-scroll flag — when `true`, streaming ticks slide X to track the
|
|
240
295
|
* data tail. Pan flips this off when the gesture pushes the tail off
|
|
241
296
|
* screen; chart's per-frame autoscroll check re-engages it when a
|
|
242
297
|
* follow-up pan brings the tail back into view.
|
|
243
298
|
*/
|
|
244
|
-
#
|
|
299
|
+
#p = !0;
|
|
245
300
|
/** Resolved viewport padding (top/bottom px + left/right pixels-or-intervals). */
|
|
246
|
-
#
|
|
301
|
+
#d;
|
|
247
302
|
/** Cached chart width — passed by interactions on every call; kept for the rare
|
|
248
303
|
* call that omits it (e.g. programmatic `chart.pan(...)` without explicit width). */
|
|
249
|
-
|
|
304
|
+
#J = 0;
|
|
250
305
|
/** Active performance monitor, or `null` when instrumentation is disabled (the default). */
|
|
251
|
-
#
|
|
306
|
+
#M;
|
|
252
307
|
/** When true, `destroy()` tears down the monitor; false for caller-supplied monitors we must not destroy. */
|
|
253
|
-
#
|
|
308
|
+
#dt = !1;
|
|
254
309
|
/** Visible HUD overlay (mounted by the perf config); non-null only when the caller requested one. */
|
|
255
|
-
#
|
|
256
|
-
constructor(t,
|
|
257
|
-
super(),
|
|
258
|
-
const
|
|
259
|
-
this.#
|
|
310
|
+
#ut = null;
|
|
311
|
+
constructor(t, e) {
|
|
312
|
+
super(), e?.axis && (this.#v = e.axis), this.#r = this.#v.x?.type ?? "time", this.#n = e?.theme ?? Zt.theme, this.#G = e?.grid?.visible !== !1, this.#C = Y(e?.fade), this.#g = at.resolve(e?.animations), this.#zt = xt(e?.viewport?.maxVisibleBars), this.#d = E(e?.padding), this.#F = new Mt();
|
|
313
|
+
const i = this.#g.axis.y, s = this.#g.axis.x;
|
|
314
|
+
this.#e = lt({
|
|
260
315
|
initial: { yRange: { min: 0, max: 0 }, xRange: { from: 0, to: 0 } },
|
|
261
316
|
y: {
|
|
262
|
-
curve:
|
|
263
|
-
settleMs:
|
|
264
|
-
stickyMs:
|
|
265
|
-
stickyFloorMs:
|
|
266
|
-
gestureMs:
|
|
267
|
-
toggleMs: this.#
|
|
317
|
+
curve: i.curve,
|
|
318
|
+
settleMs: i.settleMs,
|
|
319
|
+
stickyMs: i.stickyMs,
|
|
320
|
+
stickyFloorMs: i.stickyFloorMs,
|
|
321
|
+
gestureMs: i.gestureMs,
|
|
322
|
+
toggleMs: this.#g.toggleMs
|
|
268
323
|
},
|
|
269
324
|
x: {
|
|
270
325
|
curve: s.curve,
|
|
271
326
|
settleMs: s.settleMs,
|
|
272
327
|
gestureMs: s.gestureMs
|
|
273
328
|
},
|
|
274
|
-
computeXTarget: () => this.#
|
|
275
|
-
computeYTarget: ({ xTarget: o }) => this.#
|
|
329
|
+
computeXTarget: () => this.#we(),
|
|
330
|
+
computeYTarget: ({ xTarget: o, scale: h }) => this.#_(o, h),
|
|
276
331
|
onWake: () => this.#i?.markDirty()
|
|
277
|
-
}), this.#
|
|
278
|
-
const r =
|
|
279
|
-
this.#
|
|
280
|
-
const n = this.#
|
|
281
|
-
this.timeScale.tickTracker.setFadeMs(n), this.yScale.tickTracker.setFadeMs(n), this.#
|
|
332
|
+
}), e?.onEdgeReached && this.checkTimeOnlyFeature("onEdgeReached") && (this.#Rt = e.onEdgeReached), this.#it = e?.viewport?.initialRange;
|
|
333
|
+
const r = vt(e?.perf);
|
|
334
|
+
this.#M = r.monitor, this.#dt = r.ownsMonitor, this.#l = new ct(t, this.#M ?? void 0), this.timeScale = new Nt(), this.timeScale.setType(this.#r), this.timeScale.setBandPadding(this.#v.x?.padding), this.yScale = new U(), this.#P = j(this.yScale, this.#v.y), this.yScale.setTransform(this.#v.y?.scale ?? null), C({ y: this.#v.y, isPlottable: this.#gt(void 0) }), this.timeScale.setLocale(e?.locale), this.timeScale.setTimeZone(e?.timeZone);
|
|
335
|
+
const n = this.#g.axis.ticksMs;
|
|
336
|
+
this.timeScale.tickTracker.setFadeMs(n), this.yScale.tickTracker.setFadeMs(n), this.#Xt(e?.scales ?? {}), this.#St(), this.#E = new nt({
|
|
282
337
|
initial: 0,
|
|
283
|
-
duration: this.#
|
|
284
|
-
lerp: (o,
|
|
285
|
-
}), this.#i = new
|
|
286
|
-
(o) => this.#
|
|
287
|
-
), this.#
|
|
288
|
-
const a =
|
|
289
|
-
this.#
|
|
290
|
-
this.syncScales(), this.#
|
|
291
|
-
}), this.syncScales(), this.#
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
this.
|
|
338
|
+
duration: this.#g.axis.gridMs,
|
|
339
|
+
lerp: (o, h, l) => o + (h - o) * l
|
|
340
|
+
}), this.#i = new Z((o) => this.#wt(o)), this.#c = new Z(
|
|
341
|
+
(o) => this.#Tt("overlay", o, () => this.renderOverlay(o))
|
|
342
|
+
), this.#M && r.hud && (this.#ut = r.hud(t, this.#M));
|
|
343
|
+
const a = e?.interactive !== !1;
|
|
344
|
+
this.#V = a ? new Bt(this.#l.canvas, this, this.timeScale, this.yScale) : null, this.#l.on("resize", () => {
|
|
345
|
+
this.syncScales(), this.#wt(), this.emit("viewportChange");
|
|
346
|
+
}), this.syncScales(), this.#V?.on("crosshairMove", (o) => {
|
|
347
|
+
const h = o === null ? null : this.#mt(o);
|
|
348
|
+
this.#ot = "pointer", this.#f = h, this.#c.markDirty(), this.emit("crosshairMove", h), this.#bt(h);
|
|
349
|
+
}), this.#V?.on("click", (o) => {
|
|
350
|
+
const h = this.#mt(o);
|
|
351
|
+
this.emit("pointClick", { ...h, spatialHit: this.#Ut(h) });
|
|
352
|
+
}), this.#V?.on("dblclick", (o) => {
|
|
353
|
+
const h = this.#mt(o);
|
|
354
|
+
this.fitContent(), this.emit("pointDoubleClick", { ...h, spatialHit: this.#Ut(h) });
|
|
297
355
|
});
|
|
298
356
|
}
|
|
357
|
+
/** A pointer position on a category axis snaps to the nearest band; `x` is the coordinate in the axis domain. */
|
|
358
|
+
#mt(t) {
|
|
359
|
+
const e = this.timeScale.snapToBand(t.time);
|
|
360
|
+
return { ...t, time: e, x: this.timeScale.domainValue(e) };
|
|
361
|
+
}
|
|
299
362
|
/**
|
|
300
363
|
* Bump overlay version + emit `overlayChange`. Call from every mutation
|
|
301
364
|
* path whose output overlay components care about: data, series list,
|
|
@@ -308,9 +371,9 @@ class Ai extends At {
|
|
|
308
371
|
* `setSeriesVisible` / `setLayerVisible` calls — produce one overlay
|
|
309
372
|
* render instead of N.
|
|
310
373
|
*/
|
|
311
|
-
#
|
|
312
|
-
if (this.#
|
|
313
|
-
this.#
|
|
374
|
+
#u() {
|
|
375
|
+
if (this.#Ht++, this.#R > 0) {
|
|
376
|
+
this.#ct = !0;
|
|
314
377
|
return;
|
|
315
378
|
}
|
|
316
379
|
this.emit("overlayChange");
|
|
@@ -324,45 +387,59 @@ class Ai extends At {
|
|
|
324
387
|
* @internal
|
|
325
388
|
*/
|
|
326
389
|
getOverlayVersion() {
|
|
327
|
-
return this.#
|
|
390
|
+
return this.#Ht;
|
|
328
391
|
}
|
|
329
392
|
/**
|
|
330
393
|
* Return a series ID: use the provided hint if it's non-empty and not already taken,
|
|
331
394
|
* otherwise generate a new auto ID. Auto-generated IDs never collide with each other
|
|
332
395
|
* or with user-provided IDs because they use a monotonically increasing counter.
|
|
333
396
|
*/
|
|
334
|
-
|
|
335
|
-
if (t && !this.#t.some((
|
|
397
|
+
#ne(t) {
|
|
398
|
+
if (t && !this.#t.some((i) => i.id === t))
|
|
336
399
|
return t;
|
|
337
|
-
let
|
|
400
|
+
let e;
|
|
338
401
|
do
|
|
339
|
-
|
|
340
|
-
while (this.#t.some((
|
|
341
|
-
return
|
|
402
|
+
e = `series_${++oe}`;
|
|
403
|
+
while (this.#t.some((i) => i.id === e));
|
|
404
|
+
return e;
|
|
342
405
|
}
|
|
343
|
-
addSeries(t,
|
|
344
|
-
const
|
|
345
|
-
if (!
|
|
406
|
+
addSeries(t, e = {}) {
|
|
407
|
+
const i = typeof t == "string" ? Xt(t) : t;
|
|
408
|
+
if (!i)
|
|
346
409
|
throw new Error(
|
|
347
410
|
`[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).`
|
|
348
411
|
);
|
|
349
|
-
const { layers: s, id: r, label: n, labels: a,
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
412
|
+
const { layers: s, id: r, label: n, labels: a, scale: o, ...h } = e, l = s ?? 1;
|
|
413
|
+
o === void 0 && L({
|
|
414
|
+
seriesId: r ?? i.type,
|
|
415
|
+
stacking: h.stacking,
|
|
416
|
+
isPlottable: this.#gt(o)
|
|
417
|
+
});
|
|
418
|
+
const d = {
|
|
419
|
+
...this.#g.defaults(i.type),
|
|
420
|
+
...h,
|
|
421
|
+
...this.#g.overrides(i.type)
|
|
422
|
+
}, f = i.create({ theme: this.#n, layerCount: l }, d);
|
|
423
|
+
v(f) && Ct({ seriesType: i.type, axisType: this.#r, supported: i.xTypes ?? ["time"] });
|
|
424
|
+
const c = a ?? (typeof n == "string" ? [n] : void 0), m = this.#ne(r);
|
|
425
|
+
f.onDataChanged?.(() => this.onDataChanged());
|
|
426
|
+
const y = this.#Z();
|
|
427
|
+
return this.#t.push({
|
|
428
|
+
id: m,
|
|
429
|
+
labels: c,
|
|
430
|
+
renderer: f,
|
|
431
|
+
visible: !0,
|
|
432
|
+
scale: o,
|
|
433
|
+
xKeys: { x: !1, time: !1 },
|
|
434
|
+
categoryPoints: []
|
|
435
|
+
}), this.#z = null, this.#et(), this.#tt(y), this.emit("seriesChange"), this.#u(), m;
|
|
359
436
|
}
|
|
360
437
|
/** Remove a series by ID and clean up its resources. */
|
|
361
438
|
removeSeries(t) {
|
|
362
|
-
const
|
|
363
|
-
if (
|
|
364
|
-
const
|
|
365
|
-
this.#t[
|
|
439
|
+
const e = this.#t.findIndex((i) => i.id === t);
|
|
440
|
+
if (e >= 0) {
|
|
441
|
+
const i = this.#Z();
|
|
442
|
+
this.#t[e].renderer.dispose(), this.#t.splice(e, 1), this.#z = null, this.#r === "category" && this.#Bt(), this.#et(), this.#tt(i), this.#i.markDirty(), this.emit("seriesChange"), this.#u();
|
|
366
443
|
}
|
|
367
444
|
}
|
|
368
445
|
// --- Markers (point annotations) -----------------------------------------
|
|
@@ -376,77 +453,82 @@ class Ai extends At {
|
|
|
376
453
|
* appear in series queries.
|
|
377
454
|
*/
|
|
378
455
|
addMarker(t) {
|
|
379
|
-
const
|
|
380
|
-
return this.#
|
|
456
|
+
const e = `marker_${++he}`;
|
|
457
|
+
return this.#y.push(this.#Ft(e, t)), this.#c.markDirty(), e;
|
|
381
458
|
}
|
|
382
459
|
/** Replace a marker's config in place. No-op when the id is unknown. */
|
|
383
|
-
updateMarker(t,
|
|
384
|
-
const
|
|
385
|
-
|
|
460
|
+
updateMarker(t, e) {
|
|
461
|
+
const i = this.#y.findIndex((s) => s.id === t);
|
|
462
|
+
i !== -1 && (this.#y[i] = this.#Ft(t, e), this.#c.markDirty());
|
|
386
463
|
}
|
|
387
464
|
/** Remove a marker by id. No-op when the id is unknown. */
|
|
388
465
|
removeMarker(t) {
|
|
389
|
-
const
|
|
390
|
-
|
|
466
|
+
const e = this.#y.findIndex((i) => i.id === t);
|
|
467
|
+
e !== -1 && (this.#y.splice(e, 1), this.#c.markDirty());
|
|
391
468
|
}
|
|
392
469
|
/** Read-only snapshot of the registered markers (normalized). Mutate via add / update / removeMarker. */
|
|
393
470
|
getMarkers() {
|
|
394
|
-
return this.#
|
|
471
|
+
return this.#y;
|
|
395
472
|
}
|
|
396
|
-
#
|
|
473
|
+
#Ft(t, e) {
|
|
397
474
|
return {
|
|
398
475
|
id: t,
|
|
399
|
-
time:
|
|
400
|
-
value:
|
|
401
|
-
seriesId:
|
|
402
|
-
shape:
|
|
403
|
-
pulse:
|
|
404
|
-
label:
|
|
405
|
-
color:
|
|
476
|
+
time: k(e.time),
|
|
477
|
+
value: e.value,
|
|
478
|
+
seriesId: e.seriesId,
|
|
479
|
+
shape: e.shape ?? "dot",
|
|
480
|
+
pulse: e.pulse ?? !1,
|
|
481
|
+
label: e.label,
|
|
482
|
+
color: e.color,
|
|
483
|
+
scale: e.scale
|
|
406
484
|
};
|
|
407
485
|
}
|
|
486
|
+
/** The scale a marker's value maps through: its own `scale`, else its series' scale. */
|
|
487
|
+
#ae(t) {
|
|
488
|
+
return t.scale !== void 0 || t.seriesId === void 0 ? this.getYScale(t.scale) : this.getYScale(this.getSeriesScale(t.seriesId));
|
|
489
|
+
}
|
|
408
490
|
/** Resolve a marker's Y value: explicit `value`, else snapped to its series at `time`. Null when neither resolves. */
|
|
409
|
-
#
|
|
491
|
+
#oe(t) {
|
|
410
492
|
if (t.value !== void 0 && Number.isFinite(t.value)) return t.value;
|
|
411
493
|
if (t.seriesId === void 0) return null;
|
|
412
|
-
const
|
|
413
|
-
return
|
|
494
|
+
const e = this.getDataAtTime(t.seriesId, t.time);
|
|
495
|
+
return e ? G(e) : null;
|
|
414
496
|
}
|
|
415
497
|
/** Resolve a marker's color: explicit `color`, else the series color, else the theme line color. */
|
|
416
|
-
#
|
|
498
|
+
#he(t) {
|
|
417
499
|
if (t.color !== void 0) return t.color;
|
|
418
500
|
if (t.seriesId !== void 0) {
|
|
419
|
-
const
|
|
420
|
-
if (
|
|
501
|
+
const e = this.getSeriesColor(t.seriesId);
|
|
502
|
+
if (e !== null) return e;
|
|
421
503
|
}
|
|
422
504
|
return this.#n.line.color;
|
|
423
505
|
}
|
|
424
|
-
#
|
|
425
|
-
if (this.#
|
|
426
|
-
const s = performance.now() / (
|
|
427
|
-
for (const n of this.#
|
|
428
|
-
const a = this.#
|
|
506
|
+
#le(t, e, i) {
|
|
507
|
+
if (this.#y.length === 0) return;
|
|
508
|
+
const s = performance.now() / (ot * 2 * Math.PI) % 1, r = this.#pt();
|
|
509
|
+
for (const n of this.#y) {
|
|
510
|
+
const a = this.#oe(n);
|
|
429
511
|
if (a === null) continue;
|
|
430
|
-
const o = this.#
|
|
512
|
+
const o = this.#Yt(r, n.time);
|
|
431
513
|
if (o <= 0) continue;
|
|
432
|
-
const
|
|
433
|
-
o < 1 && (t.context.globalAlpha =
|
|
514
|
+
const h = t.context.globalAlpha;
|
|
515
|
+
o < 1 && (t.context.globalAlpha = h * o), Ot({
|
|
434
516
|
scope: t,
|
|
435
517
|
timeScale: this.timeScale,
|
|
436
|
-
yScale: this
|
|
518
|
+
yScale: this.#ae(n),
|
|
437
519
|
theme: this.#n,
|
|
438
520
|
marker: {
|
|
439
521
|
time: n.time,
|
|
440
522
|
value: a,
|
|
441
|
-
color: this.#
|
|
523
|
+
color: this.#he(n),
|
|
442
524
|
shape: n.shape,
|
|
443
525
|
pulse: n.pulse,
|
|
444
526
|
label: n.label
|
|
445
527
|
},
|
|
446
528
|
phase: s,
|
|
447
|
-
plotWidth:
|
|
448
|
-
plotHeight:
|
|
449
|
-
}), o < 1 && (t.context.globalAlpha =
|
|
529
|
+
plotWidth: e,
|
|
530
|
+
plotHeight: i
|
|
531
|
+
}), o < 1 && (t.context.globalAlpha = h);
|
|
450
532
|
}
|
|
451
533
|
}
|
|
452
534
|
// --- Time regions (shaded bands) -----------------------------------------
|
|
@@ -462,45 +544,45 @@ class Ai extends At {
|
|
|
462
544
|
* legend.
|
|
463
545
|
*/
|
|
464
546
|
addRegion(t) {
|
|
465
|
-
const
|
|
466
|
-
return this.#
|
|
547
|
+
const e = `region_${++le}`;
|
|
548
|
+
return this.checkTimeOnlyFeature("TimeRegion") && (this.#T.push(this.#Ot(e, t)), this.#i.markDirty()), e;
|
|
467
549
|
}
|
|
468
550
|
/** Replace a region's config in place. No-op when the id is unknown. */
|
|
469
|
-
updateRegion(t,
|
|
470
|
-
const
|
|
471
|
-
|
|
551
|
+
updateRegion(t, e) {
|
|
552
|
+
const i = this.#T.findIndex((s) => s.id === t);
|
|
553
|
+
i !== -1 && (this.#T[i] = this.#Ot(t, e), this.#i.markDirty());
|
|
472
554
|
}
|
|
473
555
|
/** Remove a region by id. No-op when the id is unknown. */
|
|
474
556
|
removeRegion(t) {
|
|
475
|
-
const
|
|
476
|
-
|
|
557
|
+
const e = this.#T.findIndex((i) => i.id === t);
|
|
558
|
+
e !== -1 && (this.#T.splice(e, 1), this.#i.markDirty());
|
|
477
559
|
}
|
|
478
560
|
/** Read-only snapshot of the registered regions (normalized). Mutate via add / update / removeRegion. */
|
|
479
561
|
getRegions() {
|
|
480
|
-
return this.#
|
|
562
|
+
return this.#T;
|
|
481
563
|
}
|
|
482
|
-
#
|
|
483
|
-
const { to:
|
|
564
|
+
#Ot(t, e) {
|
|
565
|
+
const { to: i } = e, s = i === void 0 || i === "now" ? null : k(i);
|
|
484
566
|
return {
|
|
485
567
|
id: t,
|
|
486
|
-
from:
|
|
568
|
+
from: k(e.from),
|
|
487
569
|
to: s,
|
|
488
|
-
fill:
|
|
489
|
-
color:
|
|
490
|
-
label:
|
|
570
|
+
fill: e.fill,
|
|
571
|
+
color: e.color,
|
|
572
|
+
label: e.label
|
|
491
573
|
};
|
|
492
574
|
}
|
|
493
|
-
#
|
|
494
|
-
if (this.#
|
|
495
|
-
for (const s of this.#
|
|
496
|
-
const { color: r, fill: n } =
|
|
497
|
-
|
|
575
|
+
#ce(t, e, i) {
|
|
576
|
+
if (this.#T.length !== 0)
|
|
577
|
+
for (const s of this.#T) {
|
|
578
|
+
const { color: r, fill: n } = Wt(s, this.#n);
|
|
579
|
+
Yt({
|
|
498
580
|
scope: t,
|
|
499
581
|
timeScale: this.timeScale,
|
|
500
582
|
theme: this.#n,
|
|
501
583
|
region: { from: s.from, to: s.to, fill: n, color: r, label: s.label },
|
|
502
|
-
plotWidth:
|
|
503
|
-
plotHeight:
|
|
584
|
+
plotWidth: e,
|
|
585
|
+
plotHeight: i
|
|
504
586
|
});
|
|
505
587
|
}
|
|
506
588
|
}
|
|
@@ -516,44 +598,45 @@ class Ai extends At {
|
|
|
516
598
|
* legend.
|
|
517
599
|
*/
|
|
518
600
|
addLine(t) {
|
|
519
|
-
const
|
|
520
|
-
return this.#
|
|
601
|
+
const e = `line_${++ce}`;
|
|
602
|
+
return this.#b.push(this.#$t(e, t)), this.#c.markDirty(), e;
|
|
521
603
|
}
|
|
522
604
|
/** Replace a reference line's config in place. No-op when the id is unknown. */
|
|
523
|
-
updateLine(t,
|
|
524
|
-
const
|
|
525
|
-
|
|
605
|
+
updateLine(t, e) {
|
|
606
|
+
const i = this.#b.findIndex((s) => s.id === t);
|
|
607
|
+
i !== -1 && (this.#b[i] = this.#$t(t, e), this.#c.markDirty());
|
|
526
608
|
}
|
|
527
609
|
/** Remove a reference line by id. No-op when the id is unknown. */
|
|
528
610
|
removeLine(t) {
|
|
529
|
-
const
|
|
530
|
-
|
|
611
|
+
const e = this.#b.findIndex((i) => i.id === t);
|
|
612
|
+
e !== -1 && (this.#b.splice(e, 1), this.#c.markDirty());
|
|
531
613
|
}
|
|
532
614
|
/** Read-only snapshot of the registered reference lines (normalized). Mutate via add / update / removeLine. */
|
|
533
615
|
getLines() {
|
|
534
|
-
return this.#
|
|
616
|
+
return this.#b;
|
|
535
617
|
}
|
|
536
|
-
|
|
537
|
-
const { time:
|
|
618
|
+
#$t(t, e) {
|
|
619
|
+
const { time: i, value: s } = e, r = {
|
|
538
620
|
id: t,
|
|
539
|
-
color:
|
|
540
|
-
label:
|
|
541
|
-
style:
|
|
542
|
-
width:
|
|
621
|
+
color: e.color,
|
|
622
|
+
label: e.label,
|
|
623
|
+
style: e.style ?? "dashed",
|
|
624
|
+
width: e.width ?? 1,
|
|
625
|
+
scale: e.scale
|
|
543
626
|
};
|
|
544
|
-
return
|
|
627
|
+
return i !== void 0 ? { ...r, orientation: "vertical", coord: k(i) } : { ...r, orientation: "horizontal", coord: s ?? Number.NaN };
|
|
545
628
|
}
|
|
546
|
-
#
|
|
547
|
-
if (this.#
|
|
548
|
-
const s = this.#
|
|
549
|
-
for (const r of this.#
|
|
550
|
-
const n = s >= 1 ? 1 : r.orientation === "vertical" ? this.#
|
|
629
|
+
#de(t, e, i) {
|
|
630
|
+
if (this.#b.length === 0) return;
|
|
631
|
+
const s = this.#pt();
|
|
632
|
+
for (const r of this.#b) {
|
|
633
|
+
const n = s >= 1 ? 1 : r.orientation === "vertical" ? this.#Yt(s, r.coord) : W(s);
|
|
551
634
|
if (n <= 0) continue;
|
|
552
635
|
const a = t.context.globalAlpha;
|
|
553
|
-
n < 1 && (t.context.globalAlpha = a * n),
|
|
636
|
+
n < 1 && (t.context.globalAlpha = a * n), $t({
|
|
554
637
|
scope: t,
|
|
555
638
|
timeScale: this.timeScale,
|
|
556
|
-
yScale: this.
|
|
639
|
+
yScale: this.getYScale(r.scale),
|
|
557
640
|
theme: this.#n,
|
|
558
641
|
line: {
|
|
559
642
|
orientation: r.orientation,
|
|
@@ -563,8 +646,8 @@ class Ai extends At {
|
|
|
563
646
|
width: r.width,
|
|
564
647
|
label: r.label
|
|
565
648
|
},
|
|
566
|
-
plotWidth:
|
|
567
|
-
plotHeight:
|
|
649
|
+
plotWidth: e,
|
|
650
|
+
plotHeight: i
|
|
568
651
|
}), n < 1 && (t.context.globalAlpha = a);
|
|
569
652
|
}
|
|
570
653
|
}
|
|
@@ -575,15 +658,23 @@ class Ai extends At {
|
|
|
575
658
|
* underlying animator mutates between frames.
|
|
576
659
|
*/
|
|
577
660
|
getAnimationState() {
|
|
578
|
-
return this.#
|
|
661
|
+
return this.#ft(this.#e.getAnimationState());
|
|
579
662
|
}
|
|
580
|
-
/** The engine animates Y in scale space (
|
|
581
|
-
#
|
|
582
|
-
const i =
|
|
583
|
-
return i === null ? t : { ...t, yRange:
|
|
663
|
+
/** The engine animates Y in scale space (the scale's forward); readers outside it get values. */
|
|
664
|
+
#ft(t, e = this.yScale) {
|
|
665
|
+
const i = e.getTransform();
|
|
666
|
+
return i === null ? t : { ...t, yRange: Ht(t.yRange, i) };
|
|
667
|
+
}
|
|
668
|
+
/** The animation state as a named scale's series see it: its own Y, in values. */
|
|
669
|
+
#Wt(t, e) {
|
|
670
|
+
const i = this.#e.getScaleYRange(e) ?? t.yRange;
|
|
671
|
+
return this.#ft({ ...t, yRange: i }, this.getYScale(e));
|
|
672
|
+
}
|
|
673
|
+
/** `isPlottable` of a scale (`undefined` = default), as a predicate for the guard helpers. */
|
|
674
|
+
#gt(t) {
|
|
675
|
+
const e = this.getYScale(t);
|
|
676
|
+
return (i) => e.isPlottable(i);
|
|
584
677
|
}
|
|
585
|
-
/** `yScale.isPlottable`, bound once for the helpers that take a predicate. */
|
|
586
|
-
#Y = (t) => this.yScale.isPlottable(t);
|
|
587
678
|
/**
|
|
588
679
|
* Reveal-front position in `[0, 1]` of any in-flight initial-load intro.
|
|
589
680
|
* With a `seriesId`, that series' own front; without, the minimum across
|
|
@@ -593,14 +684,14 @@ class Ai extends At {
|
|
|
593
684
|
*/
|
|
594
685
|
getIntroFront(t) {
|
|
595
686
|
if (t !== void 0)
|
|
596
|
-
return this.#t.find((
|
|
597
|
-
let
|
|
598
|
-
for (const
|
|
599
|
-
if (!
|
|
600
|
-
const s =
|
|
601
|
-
s !== void 0 && s <
|
|
687
|
+
return this.#t.find((i) => i.id === t)?.renderer.getIntroFront?.() ?? 1;
|
|
688
|
+
let e = 1;
|
|
689
|
+
for (const i of this.#t) {
|
|
690
|
+
if (!i.visible) continue;
|
|
691
|
+
const s = i.renderer.getIntroFront?.();
|
|
692
|
+
s !== void 0 && s < e && (e = s);
|
|
602
693
|
}
|
|
603
|
-
return
|
|
694
|
+
return e;
|
|
604
695
|
}
|
|
605
696
|
/**
|
|
606
697
|
* Reveal front for annotations (markers / reference lines). The same as
|
|
@@ -610,10 +701,10 @@ class Ai extends At {
|
|
|
610
701
|
* has actually been observed in progress, so an annotation drawn on a chart
|
|
611
702
|
* that has no data yet still rides the reveal once data arrives.
|
|
612
703
|
*/
|
|
613
|
-
#
|
|
614
|
-
if (this.#
|
|
704
|
+
#pt() {
|
|
705
|
+
if (this.#Ct) return 1;
|
|
615
706
|
const t = this.getIntroFront();
|
|
616
|
-
return t < 1 ? (this.#
|
|
707
|
+
return t < 1 ? (this.#Pt = !0, t) : (this.#Pt && (this.#Ct = !0), 1);
|
|
617
708
|
}
|
|
618
709
|
/**
|
|
619
710
|
* Entrance alpha for an overlay pinned at `time`, given the intro reveal
|
|
@@ -621,10 +712,10 @@ class Ai extends At {
|
|
|
621
712
|
* then fades in over a short band — so annotations surface in step with the
|
|
622
713
|
* series drawing past them. `1` once the intro has settled.
|
|
623
714
|
*/
|
|
624
|
-
#
|
|
715
|
+
#Yt(t, e) {
|
|
625
716
|
if (t >= 1) return 1;
|
|
626
|
-
const { from:
|
|
627
|
-
return
|
|
717
|
+
const { from: i, to: s } = this.timeScale.getRange(), r = s - i, n = r > 0 ? (e - i) / r : 0;
|
|
718
|
+
return _t((t - n) / 0.12, 0, 1);
|
|
628
719
|
}
|
|
629
720
|
/**
|
|
630
721
|
* Replace all data for a series.
|
|
@@ -634,15 +725,127 @@ class Ai extends At {
|
|
|
634
725
|
*
|
|
635
726
|
* For line/bar accepts `TimePointInput[]` (time may be `Date`); for candlestick accepts `OHLCInput[]`;
|
|
636
727
|
* for pie accepts `PieSliceData[]`. Time fields are normalized internally.
|
|
728
|
+
*
|
|
729
|
+
* On a category axis a replace is how a bar other than the last changes value,
|
|
730
|
+
* so it eases like a stream: points that keep their band ease to their new values.
|
|
637
731
|
*/
|
|
638
|
-
setSeriesData(t,
|
|
639
|
-
const s = this.#t.find((
|
|
640
|
-
|
|
732
|
+
setSeriesData(t, e, i) {
|
|
733
|
+
const s = this.#t.find((a) => a.id === t);
|
|
734
|
+
if (!s) return;
|
|
735
|
+
const r = this.#r === "category" && this.#vt(s);
|
|
736
|
+
if (this.#kt = !r, !this.#vt(s)) {
|
|
737
|
+
s.renderer.setData(e, i);
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
const n = this.#Et(s, e ?? []);
|
|
741
|
+
if (!r) {
|
|
742
|
+
s.renderer.setData(n, i);
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
this.#xt({ entry: s, layerIndex: i, mutate: () => n });
|
|
641
746
|
}
|
|
642
747
|
/** Append a new data point to the end of a series (real-time tick). */
|
|
643
|
-
appendData(t,
|
|
644
|
-
const s = this.#t.find((
|
|
645
|
-
s === void 0 || s.renderer.appendPoint === void 0
|
|
748
|
+
appendData(t, e, i) {
|
|
749
|
+
const s = this.#t.find((n) => n.id === t);
|
|
750
|
+
if (s === void 0 || s.renderer.appendPoint === void 0) return;
|
|
751
|
+
const r = s.renderer.appendPoint.bind(s.renderer);
|
|
752
|
+
this.#yt({
|
|
753
|
+
entry: s,
|
|
754
|
+
layerIndex: i,
|
|
755
|
+
points: [e],
|
|
756
|
+
apply: ([n]) => r(n, i),
|
|
757
|
+
mutateCategory: (n, a) => [...n, ...a]
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* Run a store mutation with its points mapped onto the X axis. A time axis
|
|
762
|
+
* passes them through and a numeric axis drops the ones that don't fit. A
|
|
763
|
+
* category axis applies `mutateCategory` to the layer's input-order points
|
|
764
|
+
* instead, then re-sets the store from them.
|
|
765
|
+
*/
|
|
766
|
+
#yt(t) {
|
|
767
|
+
const { entry: e, apply: i } = t;
|
|
768
|
+
if (!this.#vt(e)) {
|
|
769
|
+
i(t.points.slice());
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
const s = this.#Et(e, t.points);
|
|
773
|
+
if (s.length !== 0) {
|
|
774
|
+
if (this.#r === "numeric") {
|
|
775
|
+
i(s);
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
this.#xt({
|
|
779
|
+
entry: e,
|
|
780
|
+
layerIndex: t.layerIndex,
|
|
781
|
+
mutate: (r) => t.mutateCategory(r, s)
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
/** Replace a category layer's input-order points with `mutate`'s result and sync the categories and stores. */
|
|
786
|
+
#xt(t) {
|
|
787
|
+
const { entry: e } = t, i = t.layerIndex ?? 0;
|
|
788
|
+
e.categoryPoints[i] = t.mutate(e.categoryPoints[i] ?? []), this.#Bt({ entry: e, layerIndex: i });
|
|
789
|
+
}
|
|
790
|
+
/** Whether `entry`'s points go through the numeric / category ingest. */
|
|
791
|
+
#vt(t) {
|
|
792
|
+
return this.#r !== "time" && v(t.renderer);
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* Points that fit the X axis, a numeric one with its `x` copied to `time`.
|
|
796
|
+
* The rest are skipped with a dev warning, as is a series mixing `x` and `time`.
|
|
797
|
+
*/
|
|
798
|
+
#Et(t, e) {
|
|
799
|
+
const i = [];
|
|
800
|
+
for (const s of e) {
|
|
801
|
+
const r = s.x !== void 0;
|
|
802
|
+
if (r && (t.xKeys.x = !0), s.time !== void 0 && (t.xKeys.time = !0), !(this.#r === "category" ? typeof s.x == "string" : !r || typeof s.x == "number")) {
|
|
803
|
+
this.#I(
|
|
804
|
+
`x-misfit:${t.id}`,
|
|
805
|
+
() => Lt({ seriesId: t.id, axisType: this.#r, x: s.x })
|
|
806
|
+
);
|
|
807
|
+
continue;
|
|
808
|
+
}
|
|
809
|
+
i.push(r && this.#r === "numeric" ? { ...s, time: s.x } : s);
|
|
810
|
+
}
|
|
811
|
+
return t.xKeys.x && t.xKeys.time && this.#I(`x-mixed:${t.id}`, () => Dt({ seriesId: t.id })), i;
|
|
812
|
+
}
|
|
813
|
+
/** Category points with `time` set to their band index. */
|
|
814
|
+
#ue(t) {
|
|
815
|
+
return t.map((e) => ({ ...e, time: this.#Y.get(e.x) }));
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Re-derive the categories — first-seen order across the series (mount
|
|
819
|
+
* order, then layers, then points in input order) — and re-set every store
|
|
820
|
+
* whose band indices moved, plus the `changed` layer.
|
|
821
|
+
*/
|
|
822
|
+
#Bt(t) {
|
|
823
|
+
const e = /* @__PURE__ */ new Set();
|
|
824
|
+
for (const a of this.#t)
|
|
825
|
+
for (const o of a.categoryPoints)
|
|
826
|
+
for (const h of o ?? [])
|
|
827
|
+
e.add(h.x);
|
|
828
|
+
const i = [...e], s = this.timeScale.getCategories(), r = i.length !== s.length || i.some((a, o) => a !== s[o]), n = this.#Y;
|
|
829
|
+
this.#Y = new Map(i.map((a, o) => [a, o])), this.timeScale.setCategories(i), this.batch(() => {
|
|
830
|
+
for (const a of this.#t)
|
|
831
|
+
this.#me({ entry: a, previous: n, changed: t });
|
|
832
|
+
r && this.onDataChanged();
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
/** Re-set `entry`'s layers whose points moved band, or that `changed` names, from their input-order points. */
|
|
836
|
+
#me(t) {
|
|
837
|
+
const { entry: e, previous: i, changed: s } = t;
|
|
838
|
+
for (let r = 0; r < e.categoryPoints.length; r++) {
|
|
839
|
+
const n = e.categoryPoints[r] ?? [], a = s?.entry === e && s.layerIndex === r, o = n.some(
|
|
840
|
+
(d) => i.get(d.x) !== this.#Y.get(d.x)
|
|
841
|
+
);
|
|
842
|
+
if (!a && !o) continue;
|
|
843
|
+
const h = this.#ue(n).sort((d, f) => d.time - f.time), l = n.some((d) => {
|
|
844
|
+
const f = i.get(d.x);
|
|
845
|
+
return f !== void 0 && f !== this.#Y.get(d.x);
|
|
846
|
+
});
|
|
847
|
+
a && !l && e.renderer.morphData !== void 0 ? e.renderer.morphData(h, r) : e.renderer.setData(h, r);
|
|
848
|
+
}
|
|
646
849
|
}
|
|
647
850
|
/**
|
|
648
851
|
* Prepend older points to the front of a series — the "load more history on
|
|
@@ -657,15 +860,33 @@ class Ai extends At {
|
|
|
657
860
|
* first point; the sync reconciler guarantees this before calling here.
|
|
658
861
|
* No-op if the renderer doesn't support prepend.
|
|
659
862
|
*/
|
|
660
|
-
prependData(t,
|
|
661
|
-
const s = this.#t.find((
|
|
863
|
+
prependData(t, e, i) {
|
|
864
|
+
const s = this.#t.find((a) => a.id === t);
|
|
662
865
|
if (s === void 0 || s.renderer.prependPoints === void 0) return;
|
|
663
|
-
const r = this.#
|
|
664
|
-
r !== null && s.renderer.setHistoryHandoff !== void 0 && performance.now() - r.at <
|
|
866
|
+
const r = this.#ht.left;
|
|
867
|
+
r !== null && s.renderer.setHistoryHandoff !== void 0 && performance.now() - r.at < Qt && (s.renderer.setHistoryHandoff(r.bars), this.#B.left = !0, this.#K.left = null, this.#ht.left = null, this.#c.markDirty());
|
|
868
|
+
const n = s.renderer.prependPoints.bind(s.renderer);
|
|
869
|
+
this.#yt({
|
|
870
|
+
entry: s,
|
|
871
|
+
layerIndex: i,
|
|
872
|
+
points: e,
|
|
873
|
+
apply: (a) => n(a, i),
|
|
874
|
+
mutateCategory: (a, o) => [...o, ...a]
|
|
875
|
+
});
|
|
665
876
|
}
|
|
666
877
|
/** Update the last data point of a series in place (e.g. live candle update). */
|
|
667
|
-
updateData(t,
|
|
668
|
-
this.#t.find((
|
|
878
|
+
updateData(t, e, i) {
|
|
879
|
+
const s = this.#t.find((n) => n.id === t);
|
|
880
|
+
if (s === void 0 || s.renderer.updateLastPoint === void 0) return;
|
|
881
|
+
const r = s.renderer.updateLastPoint.bind(s.renderer);
|
|
882
|
+
this.#yt({
|
|
883
|
+
entry: s,
|
|
884
|
+
layerIndex: i,
|
|
885
|
+
points: [e],
|
|
886
|
+
apply: ([n]) => r(n, i),
|
|
887
|
+
// Like the store, an empty layer has no last point to update.
|
|
888
|
+
mutateCategory: (n, a) => n.length === 0 ? [] : [...n.slice(0, -1), ...a]
|
|
889
|
+
});
|
|
669
890
|
}
|
|
670
891
|
/**
|
|
671
892
|
* Keep only the most recent `count` points of a series — drop the oldest
|
|
@@ -680,37 +901,59 @@ class Ai extends At {
|
|
|
680
901
|
* chart.keepLast('feed', 100);
|
|
681
902
|
* ```
|
|
682
903
|
*/
|
|
683
|
-
keepLast(t,
|
|
684
|
-
this.#t.find((
|
|
904
|
+
keepLast(t, e, i) {
|
|
905
|
+
const s = this.#t.find((r) => r.id === t);
|
|
906
|
+
if (s?.renderer.keepLast !== void 0) {
|
|
907
|
+
if (this.#r !== "category") {
|
|
908
|
+
s.renderer.keepLast(e, i);
|
|
909
|
+
return;
|
|
910
|
+
}
|
|
911
|
+
this.#xt({
|
|
912
|
+
entry: s,
|
|
913
|
+
layerIndex: i,
|
|
914
|
+
mutate: (r) => e < 0 ? [...r] : r.slice(Math.max(0, r.length - e))
|
|
915
|
+
});
|
|
916
|
+
}
|
|
685
917
|
}
|
|
686
|
-
/**
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
const
|
|
692
|
-
|
|
693
|
-
const
|
|
694
|
-
(
|
|
918
|
+
/**
|
|
919
|
+
* Update visual options (color, width, etc.) for an existing series. `scale`
|
|
920
|
+
* (time series) moves it onto a named Y scale, `undefined` back to the default.
|
|
921
|
+
*/
|
|
922
|
+
updateSeriesOptions(t, e) {
|
|
923
|
+
const i = this.#t.find((u) => u.id === t);
|
|
924
|
+
if (!i) return;
|
|
925
|
+
const { scale: s, ...r } = e, n = "scale" in e, a = n ? s : i.scale;
|
|
926
|
+
if (("stacking" in e || n) && a === void 0) {
|
|
927
|
+
const u = "stacking" in e ? e.stacking : tt(i.renderer);
|
|
928
|
+
L({ seriesId: t, stacking: u, isPlottable: this.#gt(a) });
|
|
929
|
+
}
|
|
930
|
+
const o = i.labels, h = i.renderer.getLayerColors().slice(), l = this.#g.overrides(i.renderer.kind);
|
|
931
|
+
i.renderer.updateOptions({ ...r, ...l }), "label" in e && typeof e.label == "string" && (i.labels = [e.label]), this.#i.markDirty();
|
|
932
|
+
const d = this.#D(i);
|
|
933
|
+
i.scale = a;
|
|
934
|
+
const f = this.#D(i) !== d;
|
|
935
|
+
f && this.#xe();
|
|
936
|
+
const c = i.renderer.getLayerColors(), m = h.length !== c.length || h.some((u, g) => u !== c[g]), y = !Q(o, i.labels);
|
|
937
|
+
(m || y || f) && this.#u();
|
|
695
938
|
}
|
|
696
939
|
/**
|
|
697
940
|
* Batch multiple updates: suppress recomputes until `fn` returns. Exceptions
|
|
698
941
|
* inside `fn` still flush the batch so counters don't leak across calls.
|
|
699
942
|
*/
|
|
700
943
|
batch(t) {
|
|
701
|
-
this.#
|
|
944
|
+
this.#R++;
|
|
702
945
|
try {
|
|
703
946
|
t();
|
|
704
947
|
} finally {
|
|
705
|
-
if (--this.#
|
|
706
|
-
if (this.#
|
|
707
|
-
this.#
|
|
708
|
-
else if (this.#
|
|
709
|
-
this.#
|
|
710
|
-
const
|
|
711
|
-
this.#
|
|
948
|
+
if (--this.#R <= 0) {
|
|
949
|
+
if (this.#R = 0, this.#lt)
|
|
950
|
+
this.#lt = !1, this.#N = !1, this.onDataChanged();
|
|
951
|
+
else if (this.#N) {
|
|
952
|
+
this.#N = !1, this.#m = !0;
|
|
953
|
+
const e = performance.now();
|
|
954
|
+
this.#e.onSeriesVisibilityChanged(e), this.#x(e), this.#i.markDirty();
|
|
712
955
|
}
|
|
713
|
-
this.#
|
|
956
|
+
this.#ct && (this.#ct = !1, this.emit("overlayChange"));
|
|
714
957
|
}
|
|
715
958
|
}
|
|
716
959
|
}
|
|
@@ -721,18 +964,18 @@ class Ai extends At {
|
|
|
721
964
|
* computation immediately so the axis can start moving while the line
|
|
722
965
|
* fades out in parallel.
|
|
723
966
|
*/
|
|
724
|
-
setSeriesVisible(t,
|
|
725
|
-
const
|
|
726
|
-
if (!
|
|
727
|
-
|
|
728
|
-
const s = this.#
|
|
729
|
-
if (
|
|
730
|
-
this.#
|
|
967
|
+
setSeriesVisible(t, e) {
|
|
968
|
+
const i = this.#t.find((n) => n.id === t);
|
|
969
|
+
if (!i || i.visible === e) return;
|
|
970
|
+
i.visible = e, this.#u(), this.#et();
|
|
971
|
+
const s = this.#g.toggleMs;
|
|
972
|
+
if (i.renderer.setAlpha?.(e ? 1 : 0, s), this.#i.markDirty(), this.#R > 0) {
|
|
973
|
+
this.#N = !0;
|
|
731
974
|
return;
|
|
732
975
|
}
|
|
733
|
-
this.#
|
|
976
|
+
this.#m = !0;
|
|
734
977
|
const r = performance.now();
|
|
735
|
-
this.#
|
|
978
|
+
this.#e.onSeriesVisibilityChanged(r), this.#x(r);
|
|
736
979
|
}
|
|
737
980
|
/**
|
|
738
981
|
* Whether streaming ticks slide the visible range to track the data tail.
|
|
@@ -742,10 +985,10 @@ class Ai extends At {
|
|
|
742
985
|
* containing the tail.
|
|
743
986
|
*/
|
|
744
987
|
getAutoScroll() {
|
|
745
|
-
return this.#
|
|
988
|
+
return this.#p;
|
|
746
989
|
}
|
|
747
990
|
isSeriesVisible(t) {
|
|
748
|
-
return this.#t.find((
|
|
991
|
+
return this.#t.find((e) => e.id === t)?.visible ?? !0;
|
|
749
992
|
}
|
|
750
993
|
/**
|
|
751
994
|
* Show or hide a specific layer within a multi-layer series.
|
|
@@ -757,31 +1000,31 @@ class Ai extends At {
|
|
|
757
1000
|
* `getValueRange` reading `store.isVisible()`); the alpha fade keeps the
|
|
758
1001
|
* layer's geometry on screen until the engine's Y ease settles.
|
|
759
1002
|
*/
|
|
760
|
-
setLayerVisible(t,
|
|
1003
|
+
setLayerVisible(t, e, i) {
|
|
761
1004
|
const s = this.#t.find((a) => a.id === t);
|
|
762
|
-
if (!s || s.renderer.getLayerCount() <= 1 || s.renderer.isLayerVisible(
|
|
763
|
-
s.renderer.setLayerVisible(
|
|
764
|
-
const r = this.#
|
|
765
|
-
if (s.renderer.setLayerAlpha?.(
|
|
766
|
-
this.#
|
|
1005
|
+
if (!s || s.renderer.getLayerCount() <= 1 || s.renderer.isLayerVisible(e) === i) return;
|
|
1006
|
+
s.renderer.setLayerVisible(e, i), this.#u();
|
|
1007
|
+
const r = this.#g.toggleMs;
|
|
1008
|
+
if (s.renderer.setLayerAlpha?.(e, i ? 1 : 0, r), this.#i.markDirty(), this.#R > 0) {
|
|
1009
|
+
this.#N = !0;
|
|
767
1010
|
return;
|
|
768
1011
|
}
|
|
769
|
-
this.#
|
|
1012
|
+
this.#m = !0;
|
|
770
1013
|
const n = performance.now();
|
|
771
|
-
this.#
|
|
1014
|
+
this.#e.onSeriesVisibilityChanged(n), this.#x(n);
|
|
772
1015
|
}
|
|
773
|
-
isLayerVisible(t,
|
|
774
|
-
return this.#t.find((
|
|
1016
|
+
isLayerVisible(t, e) {
|
|
1017
|
+
return this.#t.find((i) => i.id === t)?.renderer.isLayerVisible(e) ?? !0;
|
|
775
1018
|
}
|
|
776
1019
|
/** Auto-fit the viewport to show all data across every series. */
|
|
777
1020
|
fitContent() {
|
|
778
|
-
const { first: t, last:
|
|
779
|
-
if (t === void 0 ||
|
|
780
|
-
const
|
|
781
|
-
this.#O(t,
|
|
1021
|
+
const { first: t, last: e } = this.getDataBounds();
|
|
1022
|
+
if (t === void 0 || e === void 0) return;
|
|
1023
|
+
const i = this.#l.size.media.width - this.#w();
|
|
1024
|
+
this.#O(t, e, i), this.#st({ xEase: !0 });
|
|
782
1025
|
}
|
|
783
1026
|
getVisibleRange() {
|
|
784
|
-
return this.#h.from === 0 && this.#h.to === 0 ? this.#
|
|
1027
|
+
return this.#h.from === 0 && this.#h.to === 0 ? this.#e.getAnimationState().xRange : this.#h;
|
|
785
1028
|
}
|
|
786
1029
|
/**
|
|
787
1030
|
* Return the full span of registered data, or `null` before any data has
|
|
@@ -789,7 +1032,7 @@ class Ai extends At {
|
|
|
789
1032
|
* tracking and is cheaper than recomputing from series stores.
|
|
790
1033
|
*/
|
|
791
1034
|
getDataRange() {
|
|
792
|
-
return this.#
|
|
1035
|
+
return this.#S === null || this.#s === null ? null : { from: this.#S, to: this.#s };
|
|
793
1036
|
}
|
|
794
1037
|
/**
|
|
795
1038
|
* Imperatively set the visible time range. Three forms:
|
|
@@ -815,35 +1058,35 @@ class Ai extends At {
|
|
|
815
1058
|
* multi-chart sync so a synced pane tracks the originating gesture in
|
|
816
1059
|
* lockstep rather than lagging behind on the long sticky contract.
|
|
817
1060
|
*/
|
|
818
|
-
setVisibleRange(t,
|
|
1061
|
+
setVisibleRange(t, e) {
|
|
819
1062
|
if (typeof t == "number") {
|
|
820
1063
|
if (!Number.isInteger(t) || t < 2) return;
|
|
821
1064
|
const { first: n, last: a } = this.getDataBounds();
|
|
822
1065
|
if (n === void 0 || a === void 0) return;
|
|
823
|
-
const o = Math.max(n, a - (t - 1) * this.#
|
|
824
|
-
this.#O(o, a,
|
|
1066
|
+
const o = Math.max(n, a - (t - 1) * this.#o), h = this.#l.size.media.width - this.#w();
|
|
1067
|
+
this.#O(o, a, h), this.#st();
|
|
825
1068
|
return;
|
|
826
1069
|
}
|
|
827
1070
|
if ("bars" in t) {
|
|
828
1071
|
if (!Number.isInteger(t.bars) || t.bars < 2) return;
|
|
829
|
-
const n =
|
|
830
|
-
this.#
|
|
1072
|
+
const n = k(t.from), a = n + t.bars * this.#o;
|
|
1073
|
+
this.#p = this.#s !== null && this.#s >= n && this.#s <= a, this.#H({ from: n, to: a }, { emitChange: !0 }), this.#L = !0, this.#st();
|
|
831
1074
|
return;
|
|
832
1075
|
}
|
|
833
|
-
const
|
|
834
|
-
this.#
|
|
835
|
-
const r = this.#
|
|
836
|
-
if (
|
|
837
|
-
r && this.#
|
|
1076
|
+
const i = k(t.from), s = k(t.to);
|
|
1077
|
+
this.#p = this.#s !== null && this.#s >= i && this.#s <= s, this.#L = !1;
|
|
1078
|
+
const r = this.#H({ from: i, to: s }, { emitChange: !0 });
|
|
1079
|
+
if (e?.gesture) {
|
|
1080
|
+
r && this.#Te();
|
|
838
1081
|
return;
|
|
839
1082
|
}
|
|
840
|
-
this.#
|
|
1083
|
+
this.#st();
|
|
841
1084
|
}
|
|
842
1085
|
getYRange() {
|
|
843
|
-
return this.#
|
|
1086
|
+
return this.#P.range;
|
|
844
1087
|
}
|
|
845
1088
|
getCrosshairPosition() {
|
|
846
|
-
return this.#
|
|
1089
|
+
return this.#f;
|
|
847
1090
|
}
|
|
848
1091
|
/**
|
|
849
1092
|
* Programmatically set or clear the crosshair. Same effect as the user
|
|
@@ -880,34 +1123,34 @@ class Ai extends At {
|
|
|
880
1123
|
*/
|
|
881
1124
|
setCrosshair(t) {
|
|
882
1125
|
if (t === null) {
|
|
883
|
-
if (this.#
|
|
884
|
-
this.#
|
|
1126
|
+
if (this.#f === null) return;
|
|
1127
|
+
this.#f = null, this.#c.markDirty(), this.emit("crosshairMove", null);
|
|
885
1128
|
return;
|
|
886
1129
|
}
|
|
887
|
-
const { time:
|
|
888
|
-
if (!Number.isFinite(
|
|
889
|
-
const
|
|
1130
|
+
const { time: e } = t;
|
|
1131
|
+
if (!Number.isFinite(e)) return;
|
|
1132
|
+
const i = this.#f;
|
|
890
1133
|
let s;
|
|
891
|
-
if (t.y !== void 0 ? s = t.y :
|
|
892
|
-
const r = this.plotLeft + this.timeScale.timeToX(
|
|
893
|
-
this.#
|
|
1134
|
+
if (t.y !== void 0 ? s = t.y : i !== null ? s = i.y : s = this.yScale.yToValue(this.yScale.getMediaHeight() / 2), !Number.isFinite(s) || i !== null && i.time === e && i.y === s) return;
|
|
1135
|
+
const r = this.plotLeft + this.timeScale.timeToX(e), n = this.yScale.valueToY(s);
|
|
1136
|
+
this.#ot = "time", this.#f = { mediaX: r, mediaY: n, time: e, x: this.timeScale.domainValue(e), y: s }, this.#c.markDirty(), this.emit("crosshairMove", this.#f);
|
|
894
1137
|
}
|
|
895
1138
|
/** Get the last visible value and whether the absolute last point is on screen. */
|
|
896
1139
|
getLastValue(t) {
|
|
897
|
-
return
|
|
1140
|
+
return gt(t, this.#t, this.#e.getAnimationState().xRange);
|
|
898
1141
|
}
|
|
899
1142
|
/** Get the second-to-last value, useful for computing change. */
|
|
900
1143
|
getPreviousClose(t) {
|
|
901
|
-
return
|
|
1144
|
+
return pt(t, this.#t);
|
|
902
1145
|
}
|
|
903
1146
|
getLastData(t) {
|
|
904
|
-
const
|
|
905
|
-
return !
|
|
1147
|
+
const e = this.#t.find((i) => i.id === t);
|
|
1148
|
+
return !e || !v(e.renderer) ? null : e.renderer.getLastDataPoint();
|
|
906
1149
|
}
|
|
907
1150
|
/** Find the data point closest to the given timestamp within one data interval. */
|
|
908
|
-
getDataAtTime(t,
|
|
909
|
-
const
|
|
910
|
-
return
|
|
1151
|
+
getDataAtTime(t, e) {
|
|
1152
|
+
const i = this.#t.find((s) => s.id === t);
|
|
1153
|
+
return i ? i.renderer.getDataAtTime?.(e, this.#X()) ?? null : null;
|
|
911
1154
|
}
|
|
912
1155
|
/**
|
|
913
1156
|
* Get all layers' data at a given time for multi-layer series (Bar/Line
|
|
@@ -915,12 +1158,21 @@ class Ai extends At {
|
|
|
915
1158
|
* snapped sample time — callers must not derive layer identity from the
|
|
916
1159
|
* array index, because hidden layers are filtered out.
|
|
917
1160
|
*/
|
|
918
|
-
getLayerSnapshots(t,
|
|
919
|
-
const
|
|
920
|
-
return
|
|
1161
|
+
getLayerSnapshots(t, e) {
|
|
1162
|
+
const i = this.#t.find((s) => s.id === t);
|
|
1163
|
+
return i?.visible ? i.renderer.getLayerSnapshots?.(e, this.#X()) ?? null : null;
|
|
1164
|
+
}
|
|
1165
|
+
/**
|
|
1166
|
+
* How far from a point the crosshair and hit-tests still find it — one data
|
|
1167
|
+
* interval on a time axis; on a category axis half a step, so a band only
|
|
1168
|
+
* finds its own point; on a numeric axis the whole data span, so a point
|
|
1169
|
+
* past a wide gap is still the nearest.
|
|
1170
|
+
*/
|
|
1171
|
+
#X() {
|
|
1172
|
+
return this.#r === "category" ? 0.5 : this.#r === "time" || this.#S === null || this.#s === null ? this.#o : Math.max(this.#o, this.#s - this.#S);
|
|
921
1173
|
}
|
|
922
1174
|
getSeriesIds() {
|
|
923
|
-
return this.#
|
|
1175
|
+
return this.#z || (this.#z = this.#t.map((t) => t.id)), this.#z.slice();
|
|
924
1176
|
}
|
|
925
1177
|
/**
|
|
926
1178
|
* True if any visible series has a time axis (line / bar / candlestick).
|
|
@@ -928,9 +1180,9 @@ class Ai extends At {
|
|
|
928
1180
|
* has no meaningful x/y coordinate system, so the dashed hairlines would
|
|
929
1181
|
* just be visual noise.
|
|
930
1182
|
*/
|
|
931
|
-
#
|
|
1183
|
+
#Nt() {
|
|
932
1184
|
for (const t of this.#t)
|
|
933
|
-
if (t.visible &&
|
|
1185
|
+
if (t.visible && v(t.renderer))
|
|
934
1186
|
return !0;
|
|
935
1187
|
return !1;
|
|
936
1188
|
}
|
|
@@ -940,26 +1192,43 @@ class Ai extends At {
|
|
|
940
1192
|
* to reserve and nothing slides under an axis column. Reads registration,
|
|
941
1193
|
* not visibility — a legend toggle must not shift the layout.
|
|
942
1194
|
*/
|
|
943
|
-
#
|
|
944
|
-
return this.#t.length === 0 ? !1 : this.#t.every((t) => !
|
|
1195
|
+
#Q() {
|
|
1196
|
+
return this.#t.length === 0 ? !1 : this.#t.every((t) => !v(t.renderer));
|
|
945
1197
|
}
|
|
946
1198
|
#Z() {
|
|
947
|
-
return {
|
|
1199
|
+
return {
|
|
1200
|
+
yAxisWidth: this.yAxisWidth,
|
|
1201
|
+
xAxisHeight: this.xAxisHeight,
|
|
1202
|
+
yAxisPosition: this.yAxisPosition,
|
|
1203
|
+
leftWidth: this.getAxisColumnWidth("left"),
|
|
1204
|
+
rightWidth: this.getAxisColumnWidth("right")
|
|
1205
|
+
};
|
|
948
1206
|
}
|
|
949
|
-
/**
|
|
950
|
-
* components re-read the layout, as they do after a resize. */
|
|
951
|
-
#
|
|
952
|
-
|
|
953
|
-
|
|
1207
|
+
/** Reassign the Y-axis columns, then resync the scales if the gutters moved
|
|
1208
|
+
* and let DOM axis / label components re-read the layout, as they do after a resize. */
|
|
1209
|
+
#tt(t) {
|
|
1210
|
+
this.#St();
|
|
1211
|
+
const e = this.#Z();
|
|
1212
|
+
(e.yAxisWidth !== t.yAxisWidth || e.xAxisHeight !== t.xAxisHeight || e.yAxisPosition !== t.yAxisPosition || e.leftWidth !== t.leftWidth || e.rightWidth !== t.rightWidth) && (this.syncScales(), this.emit("viewportChange"), this.#i.markDirty());
|
|
954
1213
|
}
|
|
955
1214
|
/**
|
|
956
1215
|
* Whether pan/zoom gestures currently apply — true when any visible series
|
|
957
|
-
* has a time axis. Part of the
|
|
958
|
-
*
|
|
959
|
-
* wheel and touch events, so the page scrolls normally under the cursor.
|
|
1216
|
+
* has a time axis and `axis.x.panZoom` allows it. Part of the
|
|
1217
|
+
* {@link PanZoomTarget} surface: otherwise the interaction layer stops
|
|
1218
|
+
* capturing wheel and touch events, so the page scrolls normally under the cursor.
|
|
960
1219
|
*/
|
|
961
1220
|
canPanZoom() {
|
|
962
|
-
return this.#
|
|
1221
|
+
return (this.#v.x?.panZoom ?? this.#r !== "category") && this.#Nt();
|
|
1222
|
+
}
|
|
1223
|
+
/**
|
|
1224
|
+
* Whether a time-only feature (Navigator, EdgeLoader, TimeRegion) can run
|
|
1225
|
+
* on this chart's X axis. On a numeric or category axis it warns once per
|
|
1226
|
+
* feature in dev and returns `false` — the caller then does nothing.
|
|
1227
|
+
*
|
|
1228
|
+
* @internal
|
|
1229
|
+
*/
|
|
1230
|
+
checkTimeOnlyFeature(t) {
|
|
1231
|
+
return this.#r === "time" ? !0 : (this.#I(`time-only:${t}`, () => Rt({ feature: t, axisType: this.#r })), !1);
|
|
963
1232
|
}
|
|
964
1233
|
/**
|
|
965
1234
|
* Push the current pan/zoom capability into the interaction layer (canvas
|
|
@@ -968,8 +1237,8 @@ class Ai extends At {
|
|
|
968
1237
|
* every mutation that can flip {@link canPanZoom}: series add/remove and
|
|
969
1238
|
* series visibility.
|
|
970
1239
|
*/
|
|
971
|
-
#
|
|
972
|
-
this.#
|
|
1240
|
+
#et() {
|
|
1241
|
+
this.#V?.syncPanZoomMode();
|
|
973
1242
|
}
|
|
974
1243
|
/**
|
|
975
1244
|
* Filter `getSeriesIds()` by renderer type. A concrete kind (`'pie'`,
|
|
@@ -982,18 +1251,18 @@ class Ai extends At {
|
|
|
982
1251
|
* - `opts.singleLayerOnly` — exclude series with more than one layer.
|
|
983
1252
|
* Useful for YLabel fallback priority (stick to a single line first).
|
|
984
1253
|
*/
|
|
985
|
-
getSeriesIdsByType(t,
|
|
986
|
-
const
|
|
1254
|
+
getSeriesIdsByType(t, e) {
|
|
1255
|
+
const i = e?.visibleOnly === !0, s = e?.singleLayerOnly === !0, r = [];
|
|
987
1256
|
for (const n of this.#t) {
|
|
988
|
-
if (!(t === "time" ?
|
|
1257
|
+
if (!(t === "time" ? v(n.renderer) : n.renderer.kind === t)) continue;
|
|
989
1258
|
const a = n.renderer.getLayerCount();
|
|
990
1259
|
if (!(s && a > 1)) {
|
|
991
|
-
if (
|
|
1260
|
+
if (i) {
|
|
992
1261
|
if (!n.visible) continue;
|
|
993
1262
|
if (a > 1) {
|
|
994
1263
|
let o = !1;
|
|
995
|
-
for (let
|
|
996
|
-
if (n.renderer.isLayerVisible(
|
|
1264
|
+
for (let h = 0; h < a; h++)
|
|
1265
|
+
if (n.renderer.isLayerVisible(h)) {
|
|
997
1266
|
o = !0;
|
|
998
1267
|
break;
|
|
999
1268
|
}
|
|
@@ -1012,7 +1281,7 @@ class Ai extends At {
|
|
|
1012
1281
|
* Returns `null` for unknown ids or empty series.
|
|
1013
1282
|
*/
|
|
1014
1283
|
getStackedLastValue(t) {
|
|
1015
|
-
return
|
|
1284
|
+
return yt(t, this.#t, this.#e.getAnimationState().xRange);
|
|
1016
1285
|
}
|
|
1017
1286
|
/**
|
|
1018
1287
|
* Stacked value at an arbitrary `time` — the cumulative stack top at that
|
|
@@ -1021,13 +1290,13 @@ class Ai extends At {
|
|
|
1021
1290
|
* (Candlestick, single-layer Line/Bar). Returns `null` for unknown ids or
|
|
1022
1291
|
* when no sample sits near `time`. Used by the YLabel intro count-up start.
|
|
1023
1292
|
*/
|
|
1024
|
-
getStackedValueAtTime(t,
|
|
1025
|
-
const
|
|
1026
|
-
if (!
|
|
1027
|
-
const s =
|
|
1293
|
+
getStackedValueAtTime(t, e) {
|
|
1294
|
+
const i = this.#t.find((n) => n.id === t);
|
|
1295
|
+
if (!i) return null;
|
|
1296
|
+
const s = i.renderer.getStackedValueAtTime?.(e, this.#X());
|
|
1028
1297
|
if (s != null) return s;
|
|
1029
|
-
const r =
|
|
1030
|
-
return r
|
|
1298
|
+
const r = i.renderer.getDataAtTime?.(e, this.#X());
|
|
1299
|
+
return !r || jt(r) ? null : G(r);
|
|
1031
1300
|
}
|
|
1032
1301
|
/**
|
|
1033
1302
|
* Per-layer last snapshots with each layer's own `time`. Returns `null`
|
|
@@ -1036,7 +1305,7 @@ class Ai extends At {
|
|
|
1036
1305
|
* when layers advance at different rates (ragged streams).
|
|
1037
1306
|
*/
|
|
1038
1307
|
getLayerLastSnapshots(t) {
|
|
1039
|
-
return this.#t.find((
|
|
1308
|
+
return this.#t.find((e) => e.id === t)?.renderer.getLayerLastSnapshots?.() ?? null;
|
|
1040
1309
|
}
|
|
1041
1310
|
/**
|
|
1042
1311
|
* Shallow view of the internal series entries so unit tests can introspect
|
|
@@ -1050,16 +1319,16 @@ class Ai extends At {
|
|
|
1050
1319
|
}
|
|
1051
1320
|
/** Get the primary display color for a series. */
|
|
1052
1321
|
getSeriesColor(t) {
|
|
1053
|
-
const
|
|
1054
|
-
return
|
|
1322
|
+
const e = this.#t.find((i) => i.id === t);
|
|
1323
|
+
return e ? e.renderer.getLayerColors()[0] ?? this.#n.line.color : null;
|
|
1055
1324
|
}
|
|
1056
1325
|
/** The series' primary name (first layer). Used for single-layer overlays. */
|
|
1057
1326
|
getSeriesLabel(t) {
|
|
1058
|
-
return this.#t.find((
|
|
1327
|
+
return this.#t.find((e) => e.id === t)?.labels?.[0];
|
|
1059
1328
|
}
|
|
1060
1329
|
/** The display name of one layer, or `undefined` when that layer is unnamed. */
|
|
1061
|
-
getLayerLabel(t,
|
|
1062
|
-
return this.#t.find((
|
|
1330
|
+
getLayerLabel(t, e) {
|
|
1331
|
+
return this.#t.find((i) => i.id === t)?.labels?.[e];
|
|
1063
1332
|
}
|
|
1064
1333
|
/**
|
|
1065
1334
|
* Set every layer's display name at once (index = layer). Drives the tooltip,
|
|
@@ -1067,9 +1336,9 @@ class Ai extends At {
|
|
|
1067
1336
|
* per-layer labels in `data` change. No-ops (and skips the overlay bump) when
|
|
1068
1337
|
* the labels are unchanged, so it's safe to call on every data tick.
|
|
1069
1338
|
*/
|
|
1070
|
-
setSeriesLabels(t,
|
|
1071
|
-
const
|
|
1072
|
-
!
|
|
1339
|
+
setSeriesLabels(t, e) {
|
|
1340
|
+
const i = this.#t.find((s) => s.id === t);
|
|
1341
|
+
!i || Q(i.labels, e) || (i.labels = e, this.#u());
|
|
1073
1342
|
}
|
|
1074
1343
|
/**
|
|
1075
1344
|
* Set the per-layer color overrides carried in `data` (sparse — `undefined`
|
|
@@ -1078,92 +1347,220 @@ class Ai extends At {
|
|
|
1078
1347
|
* the renderer, which keeps them separate from the theme base so a theme swap
|
|
1079
1348
|
* preserves them. Called by the framework wrappers; no-ops when unchanged.
|
|
1080
1349
|
*/
|
|
1081
|
-
setLayerColors(t,
|
|
1082
|
-
const
|
|
1083
|
-
!
|
|
1350
|
+
setLayerColors(t, e) {
|
|
1351
|
+
const i = this.#t.find((s) => s.id === t);
|
|
1352
|
+
!i || de(i.colorOverrides, e) || (i.colorOverrides = e, i.renderer.setColorOverrides?.(e), this.#i.markDirty(), this.#u());
|
|
1084
1353
|
}
|
|
1085
1354
|
/** Get per-layer colors for a series. Returns null for single-layer non-bar/line series. */
|
|
1086
1355
|
getSeriesLayers(t) {
|
|
1087
|
-
const
|
|
1088
|
-
if (!
|
|
1089
|
-
const
|
|
1090
|
-
if (
|
|
1091
|
-
const s =
|
|
1092
|
-
return Array.from({ length:
|
|
1356
|
+
const e = this.#t.find((r) => r.id === t);
|
|
1357
|
+
if (!e) return null;
|
|
1358
|
+
const i = e.renderer.getLayerCount();
|
|
1359
|
+
if (i <= 1) return null;
|
|
1360
|
+
const s = e.renderer.getLayerColors();
|
|
1361
|
+
return Array.from({ length: i }, (r, n) => ({ color: s[n % s.length] }));
|
|
1093
1362
|
}
|
|
1094
1363
|
/** Get all slices with computed colors and percentages. Returns null for series without slice data (e.g. candlestick, line, bar). */
|
|
1095
1364
|
getSliceInfo(t) {
|
|
1096
|
-
return this.#t.find((
|
|
1365
|
+
return this.#t.find((e) => e.id === t)?.renderer.getSliceInfo?.(this.#n) ?? null;
|
|
1097
1366
|
}
|
|
1098
1367
|
/** Get hover info (label/value/percent/color) for the currently hovered element, or null. */
|
|
1099
1368
|
getHoverInfo(t) {
|
|
1100
|
-
return this.#t.find((
|
|
1369
|
+
return this.#t.find((e) => e.id === t)?.renderer.getHoverInfo?.(this.#n) ?? null;
|
|
1101
1370
|
}
|
|
1102
1371
|
/** Apply a new theme and update series colors where appropriate. */
|
|
1103
1372
|
setTheme(t) {
|
|
1104
|
-
const
|
|
1373
|
+
const e = this.#n;
|
|
1105
1374
|
this.#n = t;
|
|
1106
|
-
for (const
|
|
1107
|
-
|
|
1108
|
-
this.#i.markDirty(), this.#
|
|
1375
|
+
for (const i of this.#t)
|
|
1376
|
+
i.renderer.applyTheme(t, e);
|
|
1377
|
+
this.#i.markDirty(), this.#u();
|
|
1109
1378
|
}
|
|
1110
1379
|
getTheme() {
|
|
1111
1380
|
return this.#n;
|
|
1112
1381
|
}
|
|
1113
1382
|
/** BCP 47 locale applied to the built-in time-axis / tooltip / crosshair formatting. */
|
|
1114
1383
|
setLocale(t) {
|
|
1115
|
-
this.timeScale.setLocale(t), this.#
|
|
1384
|
+
this.timeScale.setLocale(t), this.#u();
|
|
1116
1385
|
}
|
|
1117
1386
|
getLocale() {
|
|
1118
1387
|
return this.timeScale.getLocale();
|
|
1119
1388
|
}
|
|
1120
1389
|
/** IANA timezone applied to the same built-in date-time formatting. */
|
|
1121
1390
|
setTimeZone(t) {
|
|
1122
|
-
this.timeScale.setTimeZone(t), this.#
|
|
1391
|
+
this.timeScale.setTimeZone(t), this.#u();
|
|
1123
1392
|
}
|
|
1124
1393
|
getTimeZone() {
|
|
1125
1394
|
return this.timeScale.getTimeZone();
|
|
1126
1395
|
}
|
|
1127
1396
|
/** Update axis configuration and re-render. */
|
|
1128
1397
|
setAxis(t) {
|
|
1129
|
-
const
|
|
1130
|
-
for (const
|
|
1131
|
-
|
|
1132
|
-
const s = this.#Z(), r =
|
|
1133
|
-
this.#
|
|
1134
|
-
const
|
|
1135
|
-
|
|
1398
|
+
const e = t.y?.scale ?? null, i = (d) => _(e, d);
|
|
1399
|
+
for (const d of this.#t)
|
|
1400
|
+
!v(d.renderer) || d.scale !== void 0 || L({ seriesId: d.id, stacking: d.renderer.stacking, isPlottable: i });
|
|
1401
|
+
const s = this.#Z(), r = e !== this.yScale.getTransform(), n = t.x?.type ?? "time";
|
|
1402
|
+
n !== this.#r && this.#I("x-type", () => It({ from: this.#r, to: n }));
|
|
1403
|
+
const a = this.timeScale.getBandPadding();
|
|
1404
|
+
this.timeScale.setBandPadding(t.x?.padding);
|
|
1405
|
+
const o = this.timeScale.getBandPadding(), h = a.inner !== o.inner || a.outer !== o.outer;
|
|
1406
|
+
this.#v = t, this.#P.bounds = { min: t.y?.min, max: t.y?.max }, C({ y: t.y, isPlottable: i });
|
|
1407
|
+
const l = performance.now();
|
|
1408
|
+
r && (this.yScale.setTransform(e), this.yScale.tickTracker.reset(), this.#_() === null && this.#e.snap({ y: { min: 0, max: 0 } }, l)), this.#m = !0, this.#e.onAxisReconfig(l), this.#x(l), this.#tt(s), this.#et(), h && this.#r === "category" && this.fitContent(), this.#i.markDirty();
|
|
1409
|
+
}
|
|
1410
|
+
/**
|
|
1411
|
+
* Replace the named Y scales (see {@link ChartOptions.scales}). Added,
|
|
1412
|
+
* removed and edited scales apply in place: a changed transform resets that
|
|
1413
|
+
* scale's ticks, and every scale refits. A deep-equal replay is a no-op.
|
|
1414
|
+
*/
|
|
1415
|
+
setScales(t) {
|
|
1416
|
+
const e = t ?? {};
|
|
1417
|
+
if (Ut(e, this.#At)) return;
|
|
1418
|
+
const i = this.#Z(), s = this.#Xt(e);
|
|
1419
|
+
this.#St();
|
|
1420
|
+
const r = performance.now(), n = {};
|
|
1421
|
+
for (const a of s)
|
|
1422
|
+
this.#_(void 0, a) === null && (n[a] = { min: 0, max: 0 });
|
|
1423
|
+
this.#e.snap({ scales: n }, r), this.#m = !0, this.#e.onAxisReconfig(r), this.#x(r), this.#tt(i), this.emit("viewportChange"), this.#u(), this.#i.markDirty();
|
|
1424
|
+
}
|
|
1425
|
+
/**
|
|
1426
|
+
* Create, update and drop named-scale slots to match `next`, in its key
|
|
1427
|
+
* order. Returns the ids of existing scales whose transform changed.
|
|
1428
|
+
*/
|
|
1429
|
+
#Xt(t) {
|
|
1430
|
+
const e = /* @__PURE__ */ new Map(), i = [];
|
|
1431
|
+
for (const [s, r] of Object.entries(t)) {
|
|
1432
|
+
const n = r.scale ?? null;
|
|
1433
|
+
let a = this.#a.get(s);
|
|
1434
|
+
if (a === void 0) {
|
|
1435
|
+
const o = new U();
|
|
1436
|
+
o.setTransform(n), o.tickTracker.setFadeMs(this.#g.axis.ticksMs), a = { ...j(o, r), config: r, column: null };
|
|
1437
|
+
} else n !== a.yScale.getTransform() && (a.yScale.setTransform(n), a.yScale.tickTracker.reset(), i.push(s));
|
|
1438
|
+
a.config = { ...r }, a.bounds = { min: r.min, max: r.max }, C({
|
|
1439
|
+
y: r,
|
|
1440
|
+
isPlottable: (o) => _(n, o),
|
|
1441
|
+
path: `scales.${s}`
|
|
1442
|
+
}), e.set(s, a);
|
|
1443
|
+
}
|
|
1444
|
+
return this.#a = e, this.#At = Object.fromEntries([...e].map(([s, r]) => [s, r.config])), this.#e.setYScales([...e.keys()]), i;
|
|
1445
|
+
}
|
|
1446
|
+
/**
|
|
1447
|
+
* Hand out the Y-axis columns: the default axis keeps its side, and each
|
|
1448
|
+
* visible named scale takes its side unless an earlier one holds it —
|
|
1449
|
+
* that one becomes an overlay scale (dev warning).
|
|
1450
|
+
*/
|
|
1451
|
+
#St() {
|
|
1452
|
+
const t = /* @__PURE__ */ new Set();
|
|
1453
|
+
this.yAxisWidth > 0 && t.add(this.yAxisPosition);
|
|
1454
|
+
const e = q(this.yAxisPosition);
|
|
1455
|
+
for (const [i, s] of this.#a) {
|
|
1456
|
+
if (s.column = null, this.#j(s.config) <= 0) continue;
|
|
1457
|
+
const r = s.config.position ?? e;
|
|
1458
|
+
if (t.has(r)) {
|
|
1459
|
+
this.#I(`side:${i}`, () => kt({ scaleId: i, side: r }));
|
|
1460
|
+
continue;
|
|
1461
|
+
}
|
|
1462
|
+
t.add(r), s.column = r;
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
/** Dev warnings for a series' `scale`: an id `scales` doesn't define, a percent stack on a scale without 0. */
|
|
1466
|
+
#fe(t) {
|
|
1467
|
+
const e = t.scale;
|
|
1468
|
+
e !== void 0 && (this.#a.has(e) || this.#I(`unknown:${e}`, () => wt({ seriesId: t.id, scaleId: e })), tt(t.renderer) === "percent" && !this.getYScale(this.#D(t)).isPlottable(0) && this.#I(`percent:${t.id}`, () => Tt({ seriesId: t.id })));
|
|
1469
|
+
}
|
|
1470
|
+
#I(t, e) {
|
|
1471
|
+
this.#Mt.has(t) || (this.#Mt.add(t), e());
|
|
1472
|
+
}
|
|
1473
|
+
/** Ids of the named Y scales, in declaration order. */
|
|
1474
|
+
getYScaleIds() {
|
|
1475
|
+
return [...this.#a.keys()];
|
|
1476
|
+
}
|
|
1477
|
+
/** The Y scale named `scaleId`; the default scale for `undefined` or an id `scales` doesn't define. */
|
|
1478
|
+
getYScale(t) {
|
|
1479
|
+
return t === void 0 ? this.yScale : this.#a.get(t)?.yScale ?? this.yScale;
|
|
1480
|
+
}
|
|
1481
|
+
/**
|
|
1482
|
+
* The column a scale's axis sits in — always the default axis's for
|
|
1483
|
+
* `undefined`; `null` for an overlay scale or an unknown id.
|
|
1484
|
+
*/
|
|
1485
|
+
getYScaleColumn(t) {
|
|
1486
|
+
if (t === void 0) return { position: this.yAxisPosition, width: this.yAxisWidth };
|
|
1487
|
+
const e = this.#a.get(t);
|
|
1488
|
+
return e === void 0 || e.column === null ? null : { position: e.column, width: this.#j(e.config) };
|
|
1489
|
+
}
|
|
1490
|
+
/** The named scale a series maps through, `undefined` for the default scale. */
|
|
1491
|
+
getSeriesScale(t) {
|
|
1492
|
+
const e = this.#t.find((i) => i.id === t);
|
|
1493
|
+
return e === void 0 ? void 0 : this.#D(e);
|
|
1494
|
+
}
|
|
1495
|
+
/** `scale` if `options.scales` defines it, else `undefined` (the default scale). */
|
|
1496
|
+
#ge(t) {
|
|
1497
|
+
return t !== void 0 && this.#a.has(t) ? t : void 0;
|
|
1498
|
+
}
|
|
1499
|
+
#D(t) {
|
|
1500
|
+
return this.#ge(t.scale);
|
|
1501
|
+
}
|
|
1502
|
+
#pe(t) {
|
|
1503
|
+
return t === void 0 ? this.#P : this.#a.get(t) ?? this.#P;
|
|
1504
|
+
}
|
|
1505
|
+
/** The series a scale autoscales from. */
|
|
1506
|
+
#ye(t) {
|
|
1507
|
+
return this.#a.size === 0 ? this.#t : this.#t.filter((e) => this.#D(e) === t);
|
|
1508
|
+
}
|
|
1509
|
+
/** Snap every scale to a fresh fit — what each one holds just changed. */
|
|
1510
|
+
#xe() {
|
|
1511
|
+
this.#m = !0;
|
|
1512
|
+
const t = performance.now();
|
|
1513
|
+
this.#e.onAxisReconfig(t), this.#x(t), this.emit("viewportChange"), this.#i.markDirty();
|
|
1514
|
+
}
|
|
1515
|
+
/**
|
|
1516
|
+
* Install (or clear, with `null`) the value formatter `<YAxis format>` puts
|
|
1517
|
+
* on a scale — its axis labels, crosshair label and `YLabel` read it. `scale`
|
|
1518
|
+
* picks a named scale; an id `scales` doesn't define is a no-op.
|
|
1519
|
+
*/
|
|
1520
|
+
setYAxisFormat(t) {
|
|
1521
|
+
t.scale !== void 0 && !this.#a.has(t.scale) || (this.getYScale(t.scale).setFormat(t.format), this.#u());
|
|
1136
1522
|
}
|
|
1137
1523
|
/**
|
|
1138
1524
|
* Apply label-density knobs driven by `<YAxis labelCount=… minLabelSpacing=…>`
|
|
1139
1525
|
* — kept separate from {@link setAxis} so component props don't force a
|
|
1140
|
-
* Y-animation reset.
|
|
1141
|
-
*
|
|
1526
|
+
* Y-animation reset. `scale` picks a named scale's axis — an id `scales`
|
|
1527
|
+
* doesn't define is a no-op. Triggers a viewportChange + repaint so static
|
|
1528
|
+
* charts react without waiting for pan/zoom.
|
|
1142
1529
|
*/
|
|
1143
1530
|
setYAxisLabelDensity(t) {
|
|
1531
|
+
if (t.scale !== void 0 && !this.#a.has(t.scale)) return;
|
|
1532
|
+
const e = this.getYScale(t.scale);
|
|
1144
1533
|
let i = !1;
|
|
1145
|
-
"labelCount" in t && (
|
|
1534
|
+
"labelCount" in t && (e.setLabelCount(t.labelCount ?? null), i = !0), "minLabelSpacing" in t && (e.setMinSpacing(t.minLabelSpacing ?? null), i = !0), i && (this.syncScales(), this.emit("viewportChange"), this.#i.markDirty());
|
|
1535
|
+
}
|
|
1536
|
+
/**
|
|
1537
|
+
* Install (or clear, with `null`) the X label formatter `<XAxis format>`
|
|
1538
|
+
* puts on the axis — its tick labels, the crosshair label, the tooltip and
|
|
1539
|
+
* info-bar headers read it.
|
|
1540
|
+
*/
|
|
1541
|
+
setXAxisFormat(t) {
|
|
1542
|
+
this.timeScale.setFormat(t), this.#u();
|
|
1146
1543
|
}
|
|
1147
|
-
/** Label-density knobs for the
|
|
1544
|
+
/** Label-density knobs for the X axis — mirror of {@link setYAxisLabelDensity}. */
|
|
1148
1545
|
setTimeAxisLabelDensity(t) {
|
|
1149
|
-
let
|
|
1150
|
-
"labelCount" in t && (this.timeScale.setLabelCount(t.labelCount ?? null),
|
|
1546
|
+
let e = !1;
|
|
1547
|
+
"labelCount" in t && (this.timeScale.setLabelCount(t.labelCount ?? null), e = !0), "minLabelSpacing" in t && (this.timeScale.setMinSpacing(t.minLabelSpacing ?? null), e = !0), e && (this.syncScales(), this.emit("viewportChange"), this.#i.markDirty());
|
|
1151
1548
|
}
|
|
1152
1549
|
getMediaSize() {
|
|
1153
|
-
return this.#
|
|
1550
|
+
return this.#l.size.media;
|
|
1154
1551
|
}
|
|
1155
1552
|
/** Returns layout metrics for the chart area, Y axis, and time axis. */
|
|
1156
1553
|
getLayout() {
|
|
1157
|
-
const t = this.#
|
|
1554
|
+
const t = this.#l.size.media, e = this.yAxisWidth, i = this.xAxisHeight, s = t.width - this.#w();
|
|
1158
1555
|
return {
|
|
1159
|
-
chartArea: { x: this.plotLeft, y: 0, width:
|
|
1160
|
-
yAxisWidth:
|
|
1556
|
+
chartArea: { x: this.plotLeft, y: 0, width: s, height: t.height - i },
|
|
1557
|
+
yAxisWidth: e,
|
|
1161
1558
|
yAxisPosition: this.yAxisPosition,
|
|
1162
|
-
xAxisHeight:
|
|
1559
|
+
xAxisHeight: i
|
|
1163
1560
|
};
|
|
1164
1561
|
}
|
|
1165
1562
|
getDataInterval() {
|
|
1166
|
-
return this.#
|
|
1563
|
+
return this.#o;
|
|
1167
1564
|
}
|
|
1168
1565
|
/**
|
|
1169
1566
|
* Update viewport padding at runtime. Refits the visible time range to
|
|
@@ -1174,26 +1571,26 @@ class Ai extends At {
|
|
|
1174
1571
|
* InfoBar ResizeObserver).
|
|
1175
1572
|
*/
|
|
1176
1573
|
setPadding(t) {
|
|
1177
|
-
const
|
|
1178
|
-
this.#
|
|
1179
|
-
const
|
|
1574
|
+
const e = this.#d;
|
|
1575
|
+
this.#d = E(t);
|
|
1576
|
+
const i = this.#d, s = !B(e.left, i.left) || !B(e.right, i.right), r = e.top !== i.top || e.bottom !== i.bottom;
|
|
1180
1577
|
if (s) {
|
|
1181
1578
|
const { first: n, last: a } = this.getDataBounds();
|
|
1182
1579
|
if (n !== void 0 && a !== void 0) {
|
|
1183
|
-
const o = this.#
|
|
1580
|
+
const o = this.#l.size.media.width - this.#w();
|
|
1184
1581
|
this.#O(n, a, o);
|
|
1185
1582
|
}
|
|
1186
1583
|
}
|
|
1187
1584
|
if (s || r) {
|
|
1188
|
-
this.#
|
|
1585
|
+
this.#m = !0;
|
|
1189
1586
|
const n = performance.now();
|
|
1190
|
-
this.#
|
|
1587
|
+
this.#e.onAxisReconfig(n), this.#x(n);
|
|
1191
1588
|
}
|
|
1192
1589
|
this.syncScales(), this.#i.markDirty(), r && this.emit("viewportChange");
|
|
1193
1590
|
}
|
|
1194
1591
|
/** Show or hide the background grid. Takes effect on the next render frame. */
|
|
1195
1592
|
setGrid(t) {
|
|
1196
|
-
this.#
|
|
1593
|
+
this.#G = t.visible, this.#i.markDirty();
|
|
1197
1594
|
}
|
|
1198
1595
|
/**
|
|
1199
1596
|
* Set or clear the edge fade masks (see {@link ChartOptions.fade}).
|
|
@@ -1203,14 +1600,14 @@ class Ai extends At {
|
|
|
1203
1600
|
* the next render frame.
|
|
1204
1601
|
*/
|
|
1205
1602
|
setFade(t) {
|
|
1206
|
-
const
|
|
1207
|
-
|
|
1603
|
+
const e = Y(t), i = this.#C;
|
|
1604
|
+
e.top === i.top && e.right === i.right && e.left === i.left || (this.#C = e, this.#i.markDirty());
|
|
1208
1605
|
}
|
|
1209
1606
|
/** Report the floating header's height (see {@link headerHeight}). The
|
|
1210
1607
|
* framework wrappers keep it in sync with the measured Title / InfoBar. */
|
|
1211
1608
|
setHeaderHeight(t) {
|
|
1212
|
-
const
|
|
1213
|
-
|
|
1609
|
+
const e = Number.isFinite(t) && t > 0 ? t : 0;
|
|
1610
|
+
e !== this.#at && (this.#at = e, this.#u());
|
|
1214
1611
|
}
|
|
1215
1612
|
/**
|
|
1216
1613
|
* Set the visual state for one side of the chart. Typically called in
|
|
@@ -1221,24 +1618,24 @@ class Ai extends At {
|
|
|
1221
1618
|
* The state persists until replaced. `has-more` is accepted for API
|
|
1222
1619
|
* symmetry and currently renders identically to `idle`.
|
|
1223
1620
|
*/
|
|
1224
|
-
setEdgeState(t,
|
|
1225
|
-
if (this.#
|
|
1226
|
-
const
|
|
1227
|
-
this.#
|
|
1621
|
+
setEdgeState(t, e) {
|
|
1622
|
+
if (this.#A[t] === e || !this.checkTimeOnlyFeature("EdgeLoader")) return;
|
|
1623
|
+
const i = this.#A[t] === "loading", s = this.#B[t];
|
|
1624
|
+
this.#B[t] = !1, this.#K[t] = i && !s && (e === "idle" || e === "has-more") ? performance.now() : null, this.#A[t] = e, this.#c.markDirty();
|
|
1228
1625
|
}
|
|
1229
1626
|
/** Read the current host-declared state for a given edge. */
|
|
1230
1627
|
getEdgeState(t) {
|
|
1231
|
-
return this.#
|
|
1628
|
+
return this.#A[t];
|
|
1232
1629
|
}
|
|
1233
1630
|
/**
|
|
1234
1631
|
* Alpha for a side's `loading` exit fade, or `null` when none is in
|
|
1235
1632
|
* flight. Self-cleaning: an elapsed fade clears its start stamp.
|
|
1236
1633
|
*/
|
|
1237
|
-
#
|
|
1238
|
-
const
|
|
1239
|
-
if (
|
|
1240
|
-
const
|
|
1241
|
-
return
|
|
1634
|
+
#Zt(t) {
|
|
1635
|
+
const e = this.#K[t];
|
|
1636
|
+
if (e === null) return null;
|
|
1637
|
+
const i = (performance.now() - e) / Jt;
|
|
1638
|
+
return i >= 1 ? (this.#K[t] = null, null) : 1 - W(Math.max(0, i));
|
|
1242
1639
|
}
|
|
1243
1640
|
/**
|
|
1244
1641
|
* Swap the painter used for a side's `loading` spinner — the same
|
|
@@ -1246,8 +1643,8 @@ class Ai extends At {
|
|
|
1246
1643
|
* (default) falls back to the built-in three-dot spinner. Takes effect
|
|
1247
1644
|
* immediately if that side is currently in the `loading` state.
|
|
1248
1645
|
*/
|
|
1249
|
-
setEdgeIndicator(t,
|
|
1250
|
-
this.#
|
|
1646
|
+
setEdgeIndicator(t, e) {
|
|
1647
|
+
e !== null && !this.checkTimeOnlyFeature("EdgeLoader") || (this.#It[t] = e, this.#A[t] === "loading" && this.#c.markDirty());
|
|
1251
1648
|
}
|
|
1252
1649
|
/**
|
|
1253
1650
|
* Resolve the boundary point's Y position and series kind on the primary
|
|
@@ -1257,14 +1654,14 @@ class Ai extends At {
|
|
|
1257
1654
|
* `open` (the value immediately preceding the boundary); `right` from the
|
|
1258
1655
|
* last visible candle's `close`.
|
|
1259
1656
|
*/
|
|
1260
|
-
#
|
|
1261
|
-
const
|
|
1262
|
-
if (!
|
|
1263
|
-
const s = this.getDataAtTime(
|
|
1657
|
+
#ve(t, e) {
|
|
1658
|
+
const i = this.getSeriesIdsByType("time", { visibleOnly: !0, singleLayerOnly: !0 })[0] ?? this.getSeriesIdsByType("time", { visibleOnly: !0 })[0];
|
|
1659
|
+
if (!i) return null;
|
|
1660
|
+
const s = this.getDataAtTime(i, e);
|
|
1264
1661
|
if (!s) return null;
|
|
1265
|
-
const r = this.#t.find((n) => n.id ===
|
|
1662
|
+
const r = this.#t.find((n) => n.id === i);
|
|
1266
1663
|
return r ? {
|
|
1267
|
-
valueY: this.
|
|
1664
|
+
valueY: this.getYScale(this.#D(r)).valueToY(dt(t, s)),
|
|
1268
1665
|
seriesKind: r.renderer.kind
|
|
1269
1666
|
} : null;
|
|
1270
1667
|
}
|
|
@@ -1274,8 +1671,8 @@ class Ai extends At {
|
|
|
1274
1671
|
* placeholder shapes to match the real bars that will render once data
|
|
1275
1672
|
* lands. `null` when the viewport has no meaningful span yet.
|
|
1276
1673
|
*/
|
|
1277
|
-
#
|
|
1278
|
-
const t = this.timeScale.barWidthMedia(this.#
|
|
1674
|
+
#Se() {
|
|
1675
|
+
const t = this.timeScale.barWidthMedia(this.#o);
|
|
1279
1676
|
return Number.isFinite(t) && t > 0 ? t : null;
|
|
1280
1677
|
}
|
|
1281
1678
|
/**
|
|
@@ -1293,18 +1690,18 @@ class Ai extends At {
|
|
|
1293
1690
|
* probed, not kind-gated: a kind check here silently kills hover for the
|
|
1294
1691
|
* next spatial renderer.
|
|
1295
1692
|
*/
|
|
1296
|
-
#
|
|
1297
|
-
const
|
|
1298
|
-
top:
|
|
1299
|
-
bottom:
|
|
1693
|
+
#bt(t) {
|
|
1694
|
+
const e = this.#l.size, i = this.#d, s = {
|
|
1695
|
+
top: i.top * e.verticalPixelRatio,
|
|
1696
|
+
bottom: i.bottom * e.verticalPixelRatio
|
|
1300
1697
|
};
|
|
1301
1698
|
let r = !1, n = null;
|
|
1302
1699
|
for (const a of this.#t) {
|
|
1303
1700
|
if (!a.renderer.hitTest || !a.renderer.setHoverIndex) continue;
|
|
1304
1701
|
let o = -1;
|
|
1305
1702
|
if (t) {
|
|
1306
|
-
const
|
|
1307
|
-
o = a.renderer.hitTest(
|
|
1703
|
+
const h = (t.mediaX - this.plotLeft) * e.horizontalPixelRatio, l = t.mediaY * e.verticalPixelRatio;
|
|
1704
|
+
o = a.renderer.hitTest(h, l, e.bitmap.width, e.bitmap.height, s);
|
|
1308
1705
|
}
|
|
1309
1706
|
a.renderer.setHoverIndex(o) && (r = !0), o >= 0 && !n && (n = { seriesId: a.id, index: o, info: a.renderer.getHoverInfo?.(this.#n) ?? null });
|
|
1310
1707
|
}
|
|
@@ -1317,14 +1714,14 @@ class Ai extends At {
|
|
|
1317
1714
|
* Returns the first spatial series (pie, heatmap, or a custom spatial kind)
|
|
1318
1715
|
* whose `hitTest` claims the position, or `null` if none does.
|
|
1319
1716
|
*/
|
|
1320
|
-
#
|
|
1321
|
-
const
|
|
1322
|
-
top:
|
|
1323
|
-
bottom:
|
|
1324
|
-
}, r = (t.mediaX - this.plotLeft) *
|
|
1717
|
+
#Ut(t) {
|
|
1718
|
+
const e = this.#l.size, i = this.#d, s = {
|
|
1719
|
+
top: i.top * e.verticalPixelRatio,
|
|
1720
|
+
bottom: i.bottom * e.verticalPixelRatio
|
|
1721
|
+
}, r = (t.mediaX - this.plotLeft) * e.horizontalPixelRatio, n = t.mediaY * e.verticalPixelRatio;
|
|
1325
1722
|
for (const a of this.#t) {
|
|
1326
1723
|
if (!a.renderer.hitTest) continue;
|
|
1327
|
-
const o = a.renderer.hitTest(r, n,
|
|
1724
|
+
const o = a.renderer.hitTest(r, n, e.bitmap.width, e.bitmap.height, s);
|
|
1328
1725
|
if (o >= 0) return { seriesId: a.id, index: o };
|
|
1329
1726
|
}
|
|
1330
1727
|
return null;
|
|
@@ -1349,70 +1746,81 @@ class Ai extends At {
|
|
|
1349
1746
|
* viewport didn't move, the re-derived values are bit-identical and the
|
|
1350
1747
|
* method exits without emitting.
|
|
1351
1748
|
*/
|
|
1352
|
-
#
|
|
1353
|
-
const t = this.#
|
|
1354
|
-
|
|
1355
|
-
const
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1749
|
+
#be() {
|
|
1750
|
+
const t = this.#nt;
|
|
1751
|
+
this.#nt = !1;
|
|
1752
|
+
const e = this.#f;
|
|
1753
|
+
if (e === null) return;
|
|
1754
|
+
const { from: i, to: s } = this.timeScale.getRange(), r = this.yScale.getRange(), n = Math.abs(s - i) * 1e-9, a = Math.abs(r.max - r.min) * 1e-9, o = 1e-6;
|
|
1755
|
+
let h;
|
|
1756
|
+
if (this.#ot === "pointer") {
|
|
1757
|
+
const l = this.timeScale.snapToBand(this.timeScale.xToTime(e.mediaX - this.plotLeft)), d = this.yScale.yToValue(e.mediaY);
|
|
1758
|
+
if (!Number.isFinite(l) || !Number.isFinite(d) || Math.abs(l - e.time) <= n && Math.abs(d - e.y) <= a && !t && e.x === this.timeScale.domainValue(e.time)) return;
|
|
1759
|
+
h = { mediaX: e.mediaX, mediaY: e.mediaY, time: l, x: this.timeScale.domainValue(l), y: d };
|
|
1361
1760
|
} else {
|
|
1362
|
-
if (
|
|
1363
|
-
this.#
|
|
1761
|
+
if (e.time < i || e.time > s) {
|
|
1762
|
+
this.#f = null, this.#c.markDirty(), this.emit("crosshairMove", null), this.#bt(null);
|
|
1364
1763
|
return;
|
|
1365
1764
|
}
|
|
1366
|
-
const l = this.plotLeft + this.timeScale.timeToX(
|
|
1367
|
-
if (!Number.isFinite(l) || !Number.isFinite(
|
|
1368
|
-
|
|
1765
|
+
const l = this.plotLeft + this.timeScale.timeToX(e.time), d = this.yScale.valueToY(e.y);
|
|
1766
|
+
if (!Number.isFinite(l) || !Number.isFinite(d) || Math.abs(l - e.mediaX) <= o && Math.abs(d - e.mediaY) <= o && !t && e.x === this.timeScale.domainValue(e.time)) return;
|
|
1767
|
+
h = { mediaX: l, mediaY: d, time: e.time, x: this.timeScale.domainValue(e.time), y: e.y };
|
|
1369
1768
|
}
|
|
1370
|
-
this.#
|
|
1769
|
+
this.#f = h, this.#c.markDirty(), this.emit("crosshairMove", h), this.#bt(h);
|
|
1371
1770
|
}
|
|
1372
1771
|
/** Tear down the chart: cancel animations, remove listeners, and detach the canvas. */
|
|
1373
1772
|
destroy() {
|
|
1374
1773
|
for (const t of this.#t) t.renderer.dispose();
|
|
1375
|
-
this.#t = [], this.#
|
|
1774
|
+
this.#t = [], this.#y = [], this.#T = [], this.#b = [], this.#z = null, this.#i.destroy(), this.#c.destroy(), this.#V?.destroy(), this.#l.destroy(), this.#ut?.destroy(), this.#ut = null, this.#dt && this.#M?.destroy(), this.#M = null, this.#dt = !1, this.removeAllListeners();
|
|
1376
1775
|
}
|
|
1377
1776
|
/** The attached performance monitor, or `null` when instrumentation is disabled. */
|
|
1378
1777
|
getPerfMonitor() {
|
|
1379
|
-
return this.#
|
|
1778
|
+
return this.#M;
|
|
1380
1779
|
}
|
|
1381
1780
|
/** Compute the earliest and latest timestamps across all series. */
|
|
1382
1781
|
getDataBounds() {
|
|
1383
|
-
let t,
|
|
1384
|
-
for (const
|
|
1385
|
-
if (!
|
|
1386
|
-
const s =
|
|
1387
|
-
s !== null && ((t === void 0 || s.first < t) && (t = s.first), (
|
|
1782
|
+
let t, e;
|
|
1783
|
+
for (const i of this.#t) {
|
|
1784
|
+
if (!i.visible || !v(i.renderer)) continue;
|
|
1785
|
+
const s = i.renderer.getTimeBounds();
|
|
1786
|
+
s !== null && ((t === void 0 || s.first < t) && (t = s.first), (e === void 0 || s.last > e) && (e = s.last));
|
|
1388
1787
|
}
|
|
1389
|
-
return { first: t, last:
|
|
1788
|
+
return { first: t, last: e };
|
|
1390
1789
|
}
|
|
1391
1790
|
/** Set by `setSeriesData`/bulk-replace paths so the next `onDataChanged`
|
|
1392
1791
|
* snaps the Y range (instead of easing) and `yScale.getRange()` reflects
|
|
1393
1792
|
* the new domain synchronously. Cleared by `onDataChanged` after each run. */
|
|
1394
|
-
#
|
|
1793
|
+
#kt = !1;
|
|
1395
1794
|
onDataChanged() {
|
|
1396
|
-
if (this.#
|
|
1397
|
-
this.#
|
|
1795
|
+
if (this.#R > 0) {
|
|
1796
|
+
this.#lt = !0;
|
|
1398
1797
|
return;
|
|
1399
1798
|
}
|
|
1400
1799
|
this.updateDataInterval();
|
|
1401
|
-
const { first: t, last:
|
|
1402
|
-
t !== void 0 && (this.#
|
|
1403
|
-
const
|
|
1404
|
-
this.#
|
|
1405
|
-
const s = !this.#
|
|
1406
|
-
|
|
1800
|
+
const { first: t, last: e } = this.getDataBounds();
|
|
1801
|
+
t !== void 0 && (this.#S = t), e !== void 0 && (this.#q = this.#s, this.#s = e);
|
|
1802
|
+
const i = this.#kt;
|
|
1803
|
+
this.#kt = !1;
|
|
1804
|
+
const s = !this.#m && t !== void 0;
|
|
1805
|
+
if (i) {
|
|
1806
|
+
this.yScale.tickTracker.reset(), this.timeScale.tickTracker.reset();
|
|
1807
|
+
for (const n of this.#a.values())
|
|
1808
|
+
n.yScale.tickTracker.reset();
|
|
1809
|
+
}
|
|
1810
|
+
this.#Vt = i;
|
|
1407
1811
|
const r = performance.now();
|
|
1408
|
-
|
|
1812
|
+
i || s ? (this.#F.seed(r), this.#e.onDataReplaced(r)) : this.#e.onPointAppended(r), this.#e.lastXTarget !== null && (this.#U = !0), t !== void 0 && (this.#m = !0), this.#x(r), this.syncScales(), s ? this.#wt() : this.#i.markDirty(), this.emit("dataUpdate"), this.#u();
|
|
1409
1813
|
}
|
|
1410
1814
|
updateDataInterval() {
|
|
1815
|
+
if (this.#r === "category") {
|
|
1816
|
+
this.#o = 1;
|
|
1817
|
+
return;
|
|
1818
|
+
}
|
|
1411
1819
|
for (const t of this.#t) {
|
|
1412
|
-
if (!
|
|
1413
|
-
const
|
|
1414
|
-
if (
|
|
1415
|
-
this.#
|
|
1820
|
+
if (!v(t.renderer)) continue;
|
|
1821
|
+
const e = t.renderer.sampleTimes(20);
|
|
1822
|
+
if (e.length >= 2) {
|
|
1823
|
+
this.#o = qt(e);
|
|
1416
1824
|
break;
|
|
1417
1825
|
}
|
|
1418
1826
|
}
|
|
@@ -1422,58 +1830,51 @@ class Ai extends At {
|
|
|
1422
1830
|
* data regardless of the caller's requested kind. Also reset by paths
|
|
1423
1831
|
* that intentionally invalidate the current Y baseline (axis bound
|
|
1424
1832
|
* change, empty viewport). */
|
|
1425
|
-
#
|
|
1833
|
+
#m = !1;
|
|
1426
1834
|
/** Whether the engine's X slot has received its first emit. Pre-init X
|
|
1427
1835
|
* emits are forced to `instant` so first paint snaps to the fitted
|
|
1428
1836
|
* range — without this guard the engine would ease from `{0,0}` into
|
|
1429
1837
|
* the new target and the very first frame would render with a
|
|
1430
1838
|
* zero-width X domain. */
|
|
1431
|
-
#
|
|
1839
|
+
#U = !1;
|
|
1432
1840
|
/**
|
|
1433
1841
|
* One-shot `setVisibleRange` argument applied on the first data arrival.
|
|
1434
1842
|
* Captured at chart construction from {@link ChartOptions.viewport.initialRange};
|
|
1435
1843
|
* cleared the moment it's consumed so subsequent data swaps don't snap
|
|
1436
1844
|
* back to the initial window.
|
|
1437
1845
|
*/
|
|
1438
|
-
#
|
|
1439
|
-
/** Last Y bounds the engine resolved into {@link AnimationState.yRange}.
|
|
1440
|
-
* Used by {@link renderMain} to detect Y movement frame-to-frame so
|
|
1441
|
-
* `viewportChange` only emits when the Y range actually shifted — DOM
|
|
1442
|
-
* axis labels subscribe to that event and re-renders on every tick are
|
|
1443
|
-
* expensive. Initialised to NaN so the first real Y emit always passes
|
|
1444
|
-
* the change check. */
|
|
1445
|
-
#at = Number.NaN;
|
|
1446
|
-
#ot = Number.NaN;
|
|
1846
|
+
#it;
|
|
1447
1847
|
/** Dev warning for values the Y scale can't place — fires once per chart. */
|
|
1448
|
-
#
|
|
1848
|
+
#_t = !1;
|
|
1449
1849
|
/**
|
|
1450
|
-
* Sample the visible data window and return the resolved Y bounds
|
|
1451
|
-
* space (see {@link resolveYTarget}),
|
|
1452
|
-
* inside the X destination range.
|
|
1850
|
+
* Sample the visible data window and return the resolved Y bounds of one
|
|
1851
|
+
* scale (`undefined` = default) in its scale space (see {@link resolveYTarget}),
|
|
1852
|
+
* or `null` when none of that scale's series has data inside the X destination range.
|
|
1453
1853
|
* Sampling runs against `logicalRange` (the X target, not the animating
|
|
1454
1854
|
* current), so Y stays on a stable target while X eases — both dimensions
|
|
1455
1855
|
* converge together. Clears {@link #yInited} on empty so the next emit
|
|
1456
1856
|
* with valid data lands as an instant snap.
|
|
1457
1857
|
*/
|
|
1458
|
-
#
|
|
1459
|
-
const i = t ?? this.#h,
|
|
1460
|
-
|
|
1461
|
-
const
|
|
1462
|
-
return
|
|
1858
|
+
#_(t, e) {
|
|
1859
|
+
const i = t ?? this.#h, { yScale: s, bounds: r } = this.#pe(e), n = r.min !== void 0 && r.min !== "auto" && typeof r.min != "number" || r.max !== void 0 && r.max !== "auto" && typeof r.max != "number" ? [] : null, a = this.#ye(e), o = s.getTransform(), h = o === null ? void 0 : (d) => s.isPlottable(d);
|
|
1860
|
+
h && this.#ke({ window: i, series: a, yScale: s });
|
|
1861
|
+
const l = Vt({ targetVisible: i, series: a, allValues: n, plottable: h });
|
|
1862
|
+
return l === null ? (e === void 0 && (this.#m = !1), null) : (e !== void 0 && (this.#m = !0), zt({ raw: l, bounds: r, allValues: n ?? [], transform: o }));
|
|
1463
1863
|
}
|
|
1464
1864
|
/** Dev-only sweep behind {@link warnUnplottableValues}; stops once it has warned. */
|
|
1465
|
-
#
|
|
1466
|
-
if (
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1865
|
+
#ke(t) {
|
|
1866
|
+
if (this.#_t || process.env.NODE_ENV === "production") return;
|
|
1867
|
+
const { window: e, series: i, yScale: s } = t;
|
|
1868
|
+
for (const r of i) {
|
|
1869
|
+
if (!r.visible || !v(r.renderer)) continue;
|
|
1870
|
+
const n = r.renderer.getValueRange(e.from, e.to);
|
|
1871
|
+
if (n === null) continue;
|
|
1872
|
+
const a = (r.renderer.stacking === "normal" || r.renderer.stacking === "percent" ? n.min < 0 && !s.isPlottable(n.min) : !s.isPlottable(n.min)) ? n.min : s.isPlottable(n.max) ? null : n.max;
|
|
1873
|
+
if (a !== null) {
|
|
1874
|
+
this.#_t = !0, At({ seriesId: r.id, value: a });
|
|
1875
|
+
return;
|
|
1476
1876
|
}
|
|
1877
|
+
}
|
|
1477
1878
|
}
|
|
1478
1879
|
/**
|
|
1479
1880
|
* Engine pull-on-demand X target. Called by the engine's `computeXTarget`
|
|
@@ -1493,29 +1894,31 @@ class Ai extends At {
|
|
|
1493
1894
|
* - **autoscroll off** → `null` (no X retarget; user has panned away
|
|
1494
1895
|
* from the tail).
|
|
1495
1896
|
*/
|
|
1496
|
-
#
|
|
1497
|
-
if (this.#
|
|
1498
|
-
const { from: t, to:
|
|
1499
|
-
if (
|
|
1500
|
-
|
|
1897
|
+
#we() {
|
|
1898
|
+
if (this.#S === null || this.#s === null) return null;
|
|
1899
|
+
const { from: t, to: e } = this.#h, i = t === 0 && e === 0, s = this.#l.size.media.width - this.#w();
|
|
1900
|
+
if (this.#r === "category")
|
|
1901
|
+
return this.#O(this.#S, this.#s, s), this.#h;
|
|
1902
|
+
if (i) {
|
|
1903
|
+
if (this.#O(this.#S, this.#s, s), this.#it !== void 0 && (this.setVisibleRange(this.#it), this.#it = void 0, !this.#p && this.#s !== null)) {
|
|
1501
1904
|
const { to: n } = this.#h;
|
|
1502
|
-
this.#
|
|
1905
|
+
this.#s >= n && this.#s - n <= this.#o * 1.5 && (this.#p = !0);
|
|
1503
1906
|
}
|
|
1504
1907
|
return this.#h;
|
|
1505
1908
|
}
|
|
1506
|
-
if (!this.#
|
|
1507
|
-
if (this.#
|
|
1508
|
-
return this.#O(this.#
|
|
1509
|
-
const r =
|
|
1909
|
+
if (!this.#p) return null;
|
|
1910
|
+
if (this.#Vt)
|
|
1911
|
+
return this.#O(this.#S, this.#s, s), this.#h;
|
|
1912
|
+
const r = Pt({
|
|
1510
1913
|
currentLogical: this.#h,
|
|
1511
|
-
lastTime: this.#
|
|
1512
|
-
prevDataEnd: this.#
|
|
1513
|
-
dataInterval: this.#
|
|
1514
|
-
paddingRight: this.#
|
|
1914
|
+
lastTime: this.#s,
|
|
1915
|
+
prevDataEnd: this.#q,
|
|
1916
|
+
dataInterval: this.#o,
|
|
1917
|
+
paddingRight: this.#d.right,
|
|
1515
1918
|
chartWidth: s,
|
|
1516
|
-
holdUntilFilled: this.#
|
|
1919
|
+
holdUntilFilled: this.#L
|
|
1517
1920
|
});
|
|
1518
|
-
return r.releaseHold && (this.#
|
|
1921
|
+
return r.releaseHold && (this.#L = !1), r.reengageAutoScroll && (this.#p = !0), r.newLogical === null ? null : (this.#F.observe(performance.now()), this.#e.setXSettleMs(this.#F.pickSettleMs(this.#g.axis.x.settleMs)), this.#H(r.newLogical, { emitChange: !1, skipValidation: !0 }), this.#q = this.#s, r.newLogical);
|
|
1519
1922
|
}
|
|
1520
1923
|
/**
|
|
1521
1924
|
* Fit the viewport's logical X range around a data span. Mirrors the
|
|
@@ -1523,16 +1926,21 @@ class Ai extends At {
|
|
|
1523
1926
|
* forces autoscroll on (data tail is in the new window by construction),
|
|
1524
1927
|
* commits the new range via `viewport.setRange` (which emits `change`).
|
|
1525
1928
|
*/
|
|
1526
|
-
#O(t,
|
|
1527
|
-
const s = this
|
|
1929
|
+
#O(t, e, i) {
|
|
1930
|
+
const s = this.timeScale.bandRange();
|
|
1931
|
+
if (this.#r === "category" && s !== null) {
|
|
1932
|
+
this.#L = !1, this.#p = !0, this.#H(s, { emitChange: !0, skipValidation: !0 });
|
|
1933
|
+
return;
|
|
1934
|
+
}
|
|
1935
|
+
const r = this.#d, n = ft({
|
|
1528
1936
|
firstTime: t,
|
|
1529
|
-
lastTime:
|
|
1530
|
-
dataInterval: this.#
|
|
1531
|
-
maxVisibleBars: this.#
|
|
1532
|
-
chartWidth:
|
|
1533
|
-
padding: { left:
|
|
1937
|
+
lastTime: e,
|
|
1938
|
+
dataInterval: this.#o,
|
|
1939
|
+
maxVisibleBars: this.#zt,
|
|
1940
|
+
chartWidth: i,
|
|
1941
|
+
padding: { left: r.left, right: r.right }
|
|
1534
1942
|
});
|
|
1535
|
-
this.#
|
|
1943
|
+
this.#L = !1, this.#p = !0, this.#H(n, { emitChange: !0 });
|
|
1536
1944
|
}
|
|
1537
1945
|
/**
|
|
1538
1946
|
* Commit a programmatic zoom — `fitContent` or `setVisibleRange`. Reads
|
|
@@ -1548,15 +1956,15 @@ class Ai extends At {
|
|
|
1548
1956
|
* passes `xEase: true` so X eases instead — viewport and axis converge
|
|
1549
1957
|
* on the same frame.
|
|
1550
1958
|
*/
|
|
1551
|
-
#
|
|
1552
|
-
const
|
|
1553
|
-
if (
|
|
1554
|
-
const
|
|
1555
|
-
if (!this.#
|
|
1556
|
-
this.#
|
|
1959
|
+
#st(t) {
|
|
1960
|
+
const e = this.#h;
|
|
1961
|
+
if (e.to <= e.from) return;
|
|
1962
|
+
const i = performance.now();
|
|
1963
|
+
if (!this.#U || !this.#m) {
|
|
1964
|
+
this.#Gt(i);
|
|
1557
1965
|
return;
|
|
1558
1966
|
}
|
|
1559
|
-
this.#
|
|
1967
|
+
this.#e.onProgrammaticZoom({ xTarget: e, xEase: t?.xEase }, i), this.#x(i);
|
|
1560
1968
|
}
|
|
1561
1969
|
/**
|
|
1562
1970
|
* Commit the current logical X to the engine on the *gesture* profile:
|
|
@@ -1570,15 +1978,15 @@ class Ai extends At {
|
|
|
1570
1978
|
* instead of lagging on the long sticky contract. First paint still snaps
|
|
1571
1979
|
* (same as the programmatic path) so synchronous readers see final values.
|
|
1572
1980
|
*/
|
|
1573
|
-
#
|
|
1981
|
+
#Te() {
|
|
1574
1982
|
const t = this.#h;
|
|
1575
1983
|
if (t.to <= t.from) return;
|
|
1576
|
-
const
|
|
1577
|
-
if (!this.#
|
|
1578
|
-
this.#
|
|
1984
|
+
const e = performance.now();
|
|
1985
|
+
if (!this.#U || !this.#m) {
|
|
1986
|
+
this.#Gt(e);
|
|
1579
1987
|
return;
|
|
1580
1988
|
}
|
|
1581
|
-
this.#
|
|
1989
|
+
this.#e.onPanZoom({ xTarget: t, yAuto: !0 }, e), this.#x(e);
|
|
1582
1990
|
}
|
|
1583
1991
|
/**
|
|
1584
1992
|
* First-paint snap: land both axes on the current logical X immediately
|
|
@@ -1587,9 +1995,13 @@ class Ai extends At {
|
|
|
1587
1995
|
* commits — both must snap before the chart has painted once so a
|
|
1588
1996
|
* synchronous `getYRange()` / `getVisibleRange()` read sees final values.
|
|
1589
1997
|
*/
|
|
1590
|
-
#
|
|
1591
|
-
const
|
|
1592
|
-
|
|
1998
|
+
#Gt(t) {
|
|
1999
|
+
const e = this.#h, i = this.#_(e), s = {};
|
|
2000
|
+
for (const r of this.#a.keys()) {
|
|
2001
|
+
const n = this.#_(e, r);
|
|
2002
|
+
n !== null && (s[r] = n);
|
|
2003
|
+
}
|
|
2004
|
+
this.#U = !0, i !== null && (this.#m = !0), this.#e.snap({ x: e, y: i ?? void 0, scales: s }, t), this.#x(t);
|
|
1593
2005
|
}
|
|
1594
2006
|
/**
|
|
1595
2007
|
* Validate and commit a new logical X range. Mirrors the old
|
|
@@ -1609,9 +2021,9 @@ class Ai extends At {
|
|
|
1609
2021
|
* see `setVisibleRange`'s gesture path, where a per-frame echo would
|
|
1610
2022
|
* otherwise re-arm and stall the in-flight Y ease.
|
|
1611
2023
|
*/
|
|
1612
|
-
#
|
|
1613
|
-
const { from:
|
|
1614
|
-
return !Number.isFinite(
|
|
2024
|
+
#H(t, e) {
|
|
2025
|
+
const { from: i, to: s } = t;
|
|
2026
|
+
return !Number.isFinite(i) || !Number.isFinite(s) || s <= i || !e.skipValidation && (s - i) / this.#o < 2 || this.#h.from === i && this.#h.to === s ? !1 : (this.#h = { from: i, to: s }, e.emitChange && (this.syncScales(), this.#i?.markDirty(), this.emit("viewportChange")), !0);
|
|
1615
2027
|
}
|
|
1616
2028
|
/**
|
|
1617
2029
|
* Apply pixel-padding to the engine's Y (scale space) and return it as
|
|
@@ -1620,28 +2032,25 @@ class Ai extends At {
|
|
|
1620
2032
|
* that has an explicit (fixed) axis bound. Padding in scale space keeps
|
|
1621
2033
|
* the pixel gutters even on a log axis.
|
|
1622
2034
|
*/
|
|
1623
|
-
#
|
|
1624
|
-
const { min: i, max:
|
|
1625
|
-
min:
|
|
1626
|
-
max:
|
|
1627
|
-
},
|
|
1628
|
-
if (
|
|
1629
|
-
const m =
|
|
2035
|
+
#Ae(t) {
|
|
2036
|
+
const { slot: e, min: i, max: s, chartHeight: r } = t, { yScale: n } = e, { min: a, max: o } = this.#Me(e), h = s - i, l = r > 0 ? this.#d.top / r * h : 0, d = r > 0 ? this.#d.bottom / r * h : 0, f = {
|
|
2037
|
+
min: a ? i : i - d,
|
|
2038
|
+
max: o ? s : s + l
|
|
2039
|
+
}, c = n.getTransform();
|
|
2040
|
+
if (c === null) return f;
|
|
2041
|
+
const m = c.inverse(f.min), y = c.inverse(f.max), u = n.isPlottable(m) && m <= c.inverse(i), g = n.isPlottable(y) && y >= c.inverse(s);
|
|
1630
2042
|
return {
|
|
1631
|
-
min:
|
|
1632
|
-
max: g ?
|
|
2043
|
+
min: u ? m : c.inverse(i),
|
|
2044
|
+
max: g ? y : c.inverse(s)
|
|
1633
2045
|
};
|
|
1634
2046
|
}
|
|
1635
|
-
/** Which Y edges the user pinned — a pinned edge takes no padding. */
|
|
1636
|
-
#
|
|
2047
|
+
/** Which Y edges of a scale the user pinned — a pinned edge takes no padding. */
|
|
2048
|
+
#Me(t) {
|
|
1637
2049
|
return {
|
|
1638
|
-
min:
|
|
1639
|
-
max:
|
|
2050
|
+
min: J(t.bounds.min, t.yScale),
|
|
2051
|
+
max: J(t.bounds.max, t.yScale)
|
|
1640
2052
|
};
|
|
1641
2053
|
}
|
|
1642
|
-
#Ht(t) {
|
|
1643
|
-
return t === void 0 || t === "auto" ? !1 : !(typeof t == "number" && !this.yScale.isPlottable(t));
|
|
1644
|
-
}
|
|
1645
2054
|
/**
|
|
1646
2055
|
* Shift the visible time range by `timeDelta` ms. Hard-clamped so at
|
|
1647
2056
|
* least one data point stays on screen — no rubber-band, no overshoot.
|
|
@@ -1652,18 +2061,18 @@ class Ai extends At {
|
|
|
1652
2061
|
* as a gesture for visual easing. Public so consumers can drive pan
|
|
1653
2062
|
* programmatically.
|
|
1654
2063
|
*/
|
|
1655
|
-
pan(t,
|
|
1656
|
-
|
|
1657
|
-
const
|
|
2064
|
+
pan(t, e = this.#J) {
|
|
2065
|
+
e > 0 && (this.#J = e), this.#L = !1;
|
|
2066
|
+
const i = St({
|
|
1658
2067
|
currentLogical: this.#h,
|
|
1659
2068
|
timeDelta: t,
|
|
1660
|
-
chartWidth:
|
|
1661
|
-
dataInterval: this.#
|
|
1662
|
-
padding: { left: this.#
|
|
1663
|
-
dataStart: this.#
|
|
1664
|
-
dataEnd: this.#
|
|
2069
|
+
chartWidth: e,
|
|
2070
|
+
dataInterval: this.#o,
|
|
2071
|
+
padding: { left: this.#d.left, right: this.#d.right },
|
|
2072
|
+
dataStart: this.#S,
|
|
2073
|
+
dataEnd: this.#s
|
|
1665
2074
|
});
|
|
1666
|
-
|
|
2075
|
+
i.newLogical !== null && (this.#p = !i.autoScrollOff, this.#H(i.newLogical, { emitChange: !0 }), i.edgeReached !== null && (this.#Dt[i.edgeReached.side] = i.edgeReached.boundaryTime, this.#Rt?.(i.edgeReached)), this.#F.pause(), this.#Kt());
|
|
1667
2076
|
}
|
|
1668
2077
|
/**
|
|
1669
2078
|
* Zoom around a time anchor. `factor < 1` zooms in, `> 1` zooms out.
|
|
@@ -1680,37 +2089,37 @@ class Ai extends At {
|
|
|
1680
2089
|
* and produce a visible jump). Cursor-anchored zoom is intentionally
|
|
1681
2090
|
* sacrificed in follow-live mode — pan first to inspect history.
|
|
1682
2091
|
*/
|
|
1683
|
-
zoomAt(t,
|
|
1684
|
-
|
|
1685
|
-
const s =
|
|
2092
|
+
zoomAt(t, e, i = this.#J) {
|
|
2093
|
+
i > 0 && (this.#J = i), this.#L = !1;
|
|
2094
|
+
const s = bt({
|
|
1686
2095
|
currentLogical: this.#h,
|
|
1687
2096
|
centerTime: t,
|
|
1688
|
-
factor:
|
|
1689
|
-
chartWidth:
|
|
1690
|
-
dataInterval: this.#
|
|
1691
|
-
padding: { left: this.#
|
|
1692
|
-
dataStart: this.#
|
|
1693
|
-
dataEnd: this.#
|
|
2097
|
+
factor: e,
|
|
2098
|
+
chartWidth: i,
|
|
2099
|
+
dataInterval: this.#o,
|
|
2100
|
+
padding: { left: this.#d.left, right: this.#d.right },
|
|
2101
|
+
dataStart: this.#S,
|
|
2102
|
+
dataEnd: this.#s
|
|
1694
2103
|
});
|
|
1695
2104
|
if (s.newLogical === null) return;
|
|
1696
2105
|
let r = s.newLogical;
|
|
1697
|
-
if (this.#
|
|
1698
|
-
const n = r.to - r.from, a =
|
|
1699
|
-
r = { from: o - n, to: o }, this.#
|
|
2106
|
+
if (this.#p && this.#s !== null) {
|
|
2107
|
+
const n = r.to - r.from, a = N(this.#d.right, n, this.#o, i), o = this.#s + a;
|
|
2108
|
+
r = { from: o - n, to: o }, this.#q = this.#s;
|
|
1700
2109
|
}
|
|
1701
|
-
this.#
|
|
2110
|
+
this.#H(r, { emitChange: !0 }), this.#F.pause(), this.#Kt();
|
|
1702
2111
|
}
|
|
1703
2112
|
/**
|
|
1704
2113
|
* Common tail for `pan` / `zoomAt`: routes the just-committed `#logical`
|
|
1705
2114
|
* to the engine as a gesture so the X spring retargets and the Y spring
|
|
1706
2115
|
* eases over `animations.axis.y.gesture`.
|
|
1707
2116
|
*/
|
|
1708
|
-
#
|
|
2117
|
+
#Kt() {
|
|
1709
2118
|
const t = this.#h;
|
|
1710
2119
|
if (t.to <= t.from) return;
|
|
1711
|
-
this.#
|
|
1712
|
-
const
|
|
1713
|
-
this.#
|
|
2120
|
+
this.#U = !0, this.#m = !0;
|
|
2121
|
+
const e = performance.now();
|
|
2122
|
+
this.#e.onPanZoom({ xTarget: t, yAuto: !0 }, e), this.#x(e);
|
|
1714
2123
|
}
|
|
1715
2124
|
/**
|
|
1716
2125
|
* Tick the engine at the given wall clock and push the resolved X / Y
|
|
@@ -1725,11 +2134,27 @@ class Ai extends At {
|
|
|
1725
2134
|
* `effectiveNow === startWall` and the prune step keeps zero-duration
|
|
1726
2135
|
* events long enough for the slot processor to snap them.
|
|
1727
2136
|
*/
|
|
1728
|
-
#
|
|
1729
|
-
const
|
|
1730
|
-
if (
|
|
1731
|
-
const s =
|
|
1732
|
-
|
|
2137
|
+
#x(t) {
|
|
2138
|
+
const e = this.#e.tick(t), i = this.#l.size;
|
|
2139
|
+
if (i.media.width === 0 || i.media.height === 0) return;
|
|
2140
|
+
const s = i.media.height - this.xAxisHeight;
|
|
2141
|
+
for (const { slot: r, range: n } of this.#jt(e))
|
|
2142
|
+
this.#qt({ slot: r, range: n, chartHeight: s });
|
|
2143
|
+
this.syncScales();
|
|
2144
|
+
}
|
|
2145
|
+
/** Each scale's slot with the engine's current Y (scale space) for it. */
|
|
2146
|
+
#jt(t) {
|
|
2147
|
+
const e = [{ slot: this.#P, range: t.yRange }];
|
|
2148
|
+
for (const [i, s] of this.#a) {
|
|
2149
|
+
const r = this.#e.getScaleYRange(i);
|
|
2150
|
+
r !== null && e.push({ slot: s, range: r });
|
|
2151
|
+
}
|
|
2152
|
+
return e;
|
|
2153
|
+
}
|
|
2154
|
+
/** Pad an engine Y (scale space) into the slot's value range and remember it. */
|
|
2155
|
+
#qt(t) {
|
|
2156
|
+
const { slot: e, range: i, chartHeight: s } = t, r = e.range;
|
|
2157
|
+
e.range = this.#Ae({ slot: e, min: i.min, max: i.max, chartHeight: s }), e.prevMin = i.min, e.prevMax = i.max, (r.min !== e.range.min || r.max !== e.range.max) && e !== this.#P && (this.#nt = !0);
|
|
1733
2158
|
}
|
|
1734
2159
|
/**
|
|
1735
2160
|
* Lightweight scale sync: updates timeScale/yScale from current viewport state
|
|
@@ -1737,90 +2162,100 @@ class Ai extends At {
|
|
|
1737
2162
|
* handler, so DOM axis components always read fresh coordinates on re-render.
|
|
1738
2163
|
*/
|
|
1739
2164
|
syncScales() {
|
|
1740
|
-
const t = this.#
|
|
2165
|
+
const t = this.#l.size;
|
|
1741
2166
|
if (t.media.width === 0 || t.media.height === 0) return;
|
|
1742
|
-
const
|
|
2167
|
+
const e = t.media.width - this.#w(), i = t.media.height - this.xAxisHeight;
|
|
1743
2168
|
this.timeScale.update(
|
|
1744
|
-
this.#
|
|
1745
|
-
|
|
2169
|
+
this.#e.getAnimationState().xRange,
|
|
2170
|
+
e,
|
|
1746
2171
|
t.horizontalPixelRatio,
|
|
1747
|
-
this.#
|
|
1748
|
-
), this.yScale.update(this.#
|
|
2172
|
+
this.#o
|
|
2173
|
+
), this.yScale.update(this.#P.range, i, t.verticalPixelRatio);
|
|
2174
|
+
for (const s of this.#a.values())
|
|
2175
|
+
s.yScale.update(s.range, i, t.verticalPixelRatio);
|
|
1749
2176
|
}
|
|
1750
2177
|
/** Expensive: background, grid, all series. Only on data/viewport/resize change. */
|
|
1751
2178
|
renderMain(t) {
|
|
1752
|
-
const
|
|
1753
|
-
if (
|
|
1754
|
-
const
|
|
2179
|
+
const e = this.#l.size;
|
|
2180
|
+
if (e.media.width === 0 || e.media.height === 0) return;
|
|
2181
|
+
const i = typeof t == "number" ? t : performance.now(), s = this.#e.tick(i);
|
|
1755
2182
|
s.animating && this.#i.markDirty();
|
|
1756
|
-
const r =
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
Math.abs(c.to - (this.#e + u)) < p && (this.#m = !0);
|
|
2183
|
+
const r = e.media.height - this.xAxisHeight;
|
|
2184
|
+
let n = !1;
|
|
2185
|
+
for (const { slot: c, range: m } of this.#jt(s))
|
|
2186
|
+
m.min === c.prevMin && m.max === c.prevMax || (this.#qt({ slot: c, range: m, chartHeight: r }), n = !0);
|
|
2187
|
+
if (this.syncScales(), this.#be(), this.#s !== null && !this.#p) {
|
|
2188
|
+
const c = this.#e.lastXTarget ?? this.#h, m = c.to - c.from;
|
|
2189
|
+
if (m > 0) {
|
|
2190
|
+
const y = this.#l.size.media.width - this.#w(), u = N(this.#d.right, m, this.#o, y), g = this.#o * 0.5;
|
|
2191
|
+
Math.abs(c.to - (this.#s + u)) < g && (this.#p = !0);
|
|
1766
2192
|
}
|
|
1767
2193
|
}
|
|
1768
|
-
|
|
1769
|
-
let
|
|
1770
|
-
this.#
|
|
1771
|
-
const { context:
|
|
1772
|
-
|
|
1773
|
-
const
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
2194
|
+
n && this.emit("viewportChange");
|
|
2195
|
+
let a = !1, o = !1, h = !1, l = !1;
|
|
2196
|
+
this.#l.useMainLayer((c) => {
|
|
2197
|
+
const { context: m, bitmapSize: y } = c, u = (e.media.width - this.#w()) * e.horizontalPixelRatio, g = (e.media.height - this.xAxisHeight) * e.verticalPixelRatio, S = this.#W(c);
|
|
2198
|
+
m.clearRect(0, 0, y.width, y.height), m.save(), m.translate(S, 0), m.beginPath(), m.rect(0, 0, this.#re(c, u), g), m.clip(), this.yScale.tickTracker.setCurrentTicks(this.yScale.niceTickValues()), this.timeScale.tickTracker.setCurrentTicks(this.timeScale.niceTickValues(this.#o).ticks), this.yScale.tickTracker.tick(i), this.timeScale.tickTracker.tick(i);
|
|
2199
|
+
for (const x of this.#a.values()) {
|
|
2200
|
+
const b = x.yScale.tickTracker;
|
|
2201
|
+
b.setCurrentTicks(x.yScale.niceTickValues()), b.tick(i) && (h = !0);
|
|
2202
|
+
}
|
|
2203
|
+
const w = this.yScale.tickTracker.snapshot(), T = this.timeScale.tickTracker.snapshot();
|
|
2204
|
+
a = w.isAnimating, o = T.isAnimating, (w.isAnimating || T.isAnimating || h || s.animating) && this.emit("tickFrame");
|
|
2205
|
+
const A = this.#G ? 1 : 0;
|
|
2206
|
+
if ((A === 0 || w.entries.length > 0 || T.entries.length > 0) && A !== this.#E.target) {
|
|
2207
|
+
const x = ht() ? 0 : this.#g.axis.gridMs;
|
|
2208
|
+
this.#E.setTarget(A, { now: i, duration: x });
|
|
1779
2209
|
}
|
|
1780
|
-
|
|
1781
|
-
const
|
|
2210
|
+
l = this.#E.tick(i);
|
|
2211
|
+
const R = this.#E.current, I = this.#r === "category" ? ie : T, H = {
|
|
1782
2212
|
scope: c,
|
|
1783
2213
|
timeScale: this.timeScale,
|
|
1784
2214
|
yScale: this.yScale,
|
|
1785
2215
|
theme: this.#n,
|
|
1786
|
-
yTicks:
|
|
1787
|
-
timeTicks:
|
|
1788
|
-
alpha:
|
|
1789
|
-
},
|
|
1790
|
-
|
|
1791
|
-
const
|
|
1792
|
-
for (const
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
2216
|
+
yTicks: w,
|
|
2217
|
+
timeTicks: I,
|
|
2218
|
+
alpha: R
|
|
2219
|
+
}, M = this.#ie(c, u);
|
|
2220
|
+
M === 0 && X(H), this.#ce(c, u, g);
|
|
2221
|
+
const V = this.#d, et = { top: V.top, bottom: V.bottom }, it = this.#ft(s), z = this.#M;
|
|
2222
|
+
for (const x of this.#t) {
|
|
2223
|
+
this.#fe(x);
|
|
2224
|
+
const b = x.renderer.getAlpha?.() ?? (x.visible ? 1 : 0);
|
|
2225
|
+
if (b <= 0) continue;
|
|
2226
|
+
const P = this.#D(x), st = this.getYScale(P), O = {
|
|
1796
2227
|
scope: c,
|
|
1797
2228
|
timeScale: this.timeScale,
|
|
1798
|
-
yScale:
|
|
2229
|
+
yScale: st,
|
|
1799
2230
|
theme: this.#n,
|
|
1800
|
-
dataInterval: this.#
|
|
1801
|
-
padding:
|
|
1802
|
-
state:
|
|
1803
|
-
seriesId:
|
|
1804
|
-
},
|
|
1805
|
-
if (
|
|
1806
|
-
const
|
|
1807
|
-
|
|
2231
|
+
dataInterval: this.#o,
|
|
2232
|
+
padding: et,
|
|
2233
|
+
state: P === void 0 ? it : this.#Wt(s, P),
|
|
2234
|
+
seriesId: x.id
|
|
2235
|
+
}, $ = m.globalAlpha;
|
|
2236
|
+
if (b < 1 && (m.globalAlpha = $ * b), z) {
|
|
2237
|
+
const rt = performance.now();
|
|
2238
|
+
x.renderer.render(O), z.recordSeries(x.id, performance.now() - rt, i);
|
|
1808
2239
|
} else
|
|
1809
|
-
|
|
1810
|
-
|
|
2240
|
+
x.renderer.render(O);
|
|
2241
|
+
b < 1 && (m.globalAlpha = $);
|
|
1811
2242
|
}
|
|
1812
|
-
|
|
1813
|
-
const
|
|
1814
|
-
|
|
1815
|
-
}), this.#
|
|
2243
|
+
m.restore();
|
|
2244
|
+
const F = { left: S, width: u, height: g };
|
|
2245
|
+
M === 0 ? (this.#Qt({ scope: c, pane: F, timeTicks: I, alpha: R }), this.#$(c, u, "left"), this.#$(c, u, "right")) : (this.#$(c, u, "left"), this.#Le({ grid: H, pane: F, overhang: M }), this.#$(c, u, "right")), this.#ee(c, u), this.#Jt(c, g);
|
|
2246
|
+
}), this.#pt();
|
|
1816
2247
|
let d = !1;
|
|
1817
2248
|
for (const c of this.#t)
|
|
1818
2249
|
if (c.renderer.needsAnimation) {
|
|
1819
2250
|
d = !0, this.#i.markDirty();
|
|
1820
2251
|
break;
|
|
1821
2252
|
}
|
|
1822
|
-
const
|
|
1823
|
-
(
|
|
2253
|
+
const f = a || o || h;
|
|
2254
|
+
if ((f || l) && this.#i.markDirty(), !s.animating && !d && !f) {
|
|
2255
|
+
this.yScale.tickTracker.markArmed(), this.timeScale.tickTracker.markArmed();
|
|
2256
|
+
for (const c of this.#a.values())
|
|
2257
|
+
c.yScale.tickTracker.markArmed();
|
|
2258
|
+
}
|
|
1824
2259
|
}
|
|
1825
2260
|
/**
|
|
1826
2261
|
* Erase the main layer's alpha across the top fade zone so content
|
|
@@ -1829,12 +2264,12 @@ class Ai extends At {
|
|
|
1829
2264
|
* a paint-over — so the container background behind the canvas (solid or
|
|
1830
2265
|
* CSS gradient) shows through untouched.
|
|
1831
2266
|
*/
|
|
1832
|
-
#
|
|
1833
|
-
if (this.#
|
|
1834
|
-
const
|
|
1835
|
-
if (
|
|
2267
|
+
#Jt(t, e) {
|
|
2268
|
+
if (this.#C.top <= 0) return;
|
|
2269
|
+
const i = Math.min(Math.round(this.#C.top * t.verticalPixelRatio), Math.round(e));
|
|
2270
|
+
if (i < 1) return;
|
|
1836
2271
|
const { context: s } = t;
|
|
1837
|
-
s.save(), s.globalCompositeOperation = "destination-out", s.fillStyle = this.#
|
|
2272
|
+
s.save(), s.globalCompositeOperation = "destination-out", s.fillStyle = this.#Re(s, i), s.fillRect(0, 0, t.bitmapSize.width, i), s.restore();
|
|
1838
2273
|
}
|
|
1839
2274
|
/**
|
|
1840
2275
|
* Run the vertical gridlines a short stub past the pane floor, dissolving
|
|
@@ -1844,23 +2279,23 @@ class Ai extends At {
|
|
|
1844
2279
|
* nothing else paints) and faded with the same destination-out ramp the
|
|
1845
2280
|
* edge masks use, so the taper stays correct over any background.
|
|
1846
2281
|
*/
|
|
1847
|
-
#
|
|
1848
|
-
const { scope:
|
|
1849
|
-
if (!this.#
|
|
1850
|
-
const { context: n, horizontalPixelRatio: a, verticalPixelRatio: o } =
|
|
1851
|
-
if (
|
|
2282
|
+
#Qt(t) {
|
|
2283
|
+
const { scope: e, pane: i, timeTicks: s, alpha: r } = t;
|
|
2284
|
+
if (!this.#G && r <= 0.01 || this.xAxisHeight === 0) return;
|
|
2285
|
+
const { context: n, horizontalPixelRatio: a, verticalPixelRatio: o } = e, h = Math.round(i.height), l = Math.round(D * o);
|
|
2286
|
+
if (l < 1) return;
|
|
1852
2287
|
const d = this.#n.grid;
|
|
1853
|
-
n.save(), n.strokeStyle = d.color, d.style === "dashed" ? n.setLineDash([4 * a, 4 * a]) : d.style === "dotted" && n.setLineDash([1 * a, 3 * a]), n.lineDashOffset =
|
|
1854
|
-
const
|
|
1855
|
-
for (const { value:
|
|
1856
|
-
const u =
|
|
2288
|
+
n.save(), n.strokeStyle = d.color, d.style === "dashed" ? n.setLineDash([4 * a, 4 * a]) : d.style === "dotted" && n.setLineDash([1 * a, 3 * a]), n.lineDashOffset = h, n.lineWidth = Gt(a);
|
|
2289
|
+
const f = Kt(a), c = Math.round(i.width);
|
|
2290
|
+
for (const { value: m, opacity: y } of s.entries) {
|
|
2291
|
+
const u = y * r;
|
|
1857
2292
|
if (u <= 0.01) continue;
|
|
1858
|
-
const
|
|
1859
|
-
if (
|
|
1860
|
-
const S =
|
|
1861
|
-
n.globalAlpha = u, n.beginPath(), n.moveTo(S,
|
|
2293
|
+
const g = Math.round(this.timeScale.timeToBitmapX(m)) + f;
|
|
2294
|
+
if (g < 0 || g > c) continue;
|
|
2295
|
+
const S = i.left + g;
|
|
2296
|
+
n.globalAlpha = u, n.beginPath(), n.moveTo(S, h), n.lineTo(S, h + l), n.stroke();
|
|
1862
2297
|
}
|
|
1863
|
-
n.restore(), this.#
|
|
2298
|
+
n.restore(), this.#te(e, i.height);
|
|
1864
2299
|
}
|
|
1865
2300
|
/**
|
|
1866
2301
|
* Erase ramp over the below-pane overrun strip — the gridline tail stubs
|
|
@@ -1868,12 +2303,12 @@ class Ai extends At {
|
|
|
1868
2303
|
* erased at the pane floor, total at the strip's bottom edge, so whatever
|
|
1869
2304
|
* crosses the floor tapers out short of the time-axis labels.
|
|
1870
2305
|
*/
|
|
1871
|
-
#
|
|
2306
|
+
#te(t, e) {
|
|
1872
2307
|
if (this.xAxisHeight === 0) return;
|
|
1873
|
-
const
|
|
2308
|
+
const i = Math.round(e), s = Math.round(D * t.verticalPixelRatio);
|
|
1874
2309
|
if (s < 1) return;
|
|
1875
2310
|
const { context: r } = t;
|
|
1876
|
-
r.save(), r.globalCompositeOperation = "destination-out", r.fillStyle = this.#
|
|
2311
|
+
r.save(), r.globalCompositeOperation = "destination-out", r.fillStyle = this.#He({ context: r, start: i, height: s }), r.fillRect(0, i, t.bitmapSize.width, s), r.restore();
|
|
1877
2312
|
}
|
|
1878
2313
|
/**
|
|
1879
2314
|
* Crosshair hairlines with the same edge melt the main layer runs: the
|
|
@@ -1885,18 +2320,18 @@ class Ai extends At {
|
|
|
1885
2320
|
* edge. Erases alpha (destination-out), so it must run while the
|
|
1886
2321
|
* crosshair is the only content on the layer.
|
|
1887
2322
|
*/
|
|
1888
|
-
#
|
|
1889
|
-
const
|
|
1890
|
-
if (
|
|
1891
|
-
const { scope:
|
|
1892
|
-
n.save(), n.translate(a, 0), n.beginPath(), n.rect(-
|
|
1893
|
-
scope:
|
|
1894
|
-
bitmapX:
|
|
1895
|
-
bitmapY:
|
|
2323
|
+
#Pe(t) {
|
|
2324
|
+
const e = this.#f;
|
|
2325
|
+
if (e === null) return;
|
|
2326
|
+
const { scope: i, chartBitmapWidth: s, chartBitmapHeight: r } = t, { context: n } = i, a = this.#W(i), o = this.#re(i, s), h = this.#ie(i, s), l = this.xAxisHeight > 0 ? Math.round(D * i.verticalPixelRatio) : 0;
|
|
2327
|
+
n.save(), n.translate(a, 0), n.beginPath(), n.rect(-h, 0, o + h, r + l), n.clip(), Ft({
|
|
2328
|
+
scope: i,
|
|
2329
|
+
bitmapX: e.mediaX * i.horizontalPixelRatio - a,
|
|
2330
|
+
bitmapY: e.mediaY * i.verticalPixelRatio,
|
|
1896
2331
|
theme: this.#n,
|
|
1897
2332
|
pane: { width: s, height: r },
|
|
1898
|
-
leftOverhang:
|
|
1899
|
-
}), n.restore(), this.#
|
|
2333
|
+
leftOverhang: h
|
|
2334
|
+
}), n.restore(), this.#te(i, r), h === 0 ? this.#$(i, s, "left") : this.#se(i, h), this.#$(i, s, "right"), this.#ee(i, s), this.#Jt(i, r);
|
|
1900
2335
|
}
|
|
1901
2336
|
/**
|
|
1902
2337
|
* Horizontal companion to {@link #applyTopFade}: dissolve content that
|
|
@@ -1905,88 +2340,93 @@ class Ai extends At {
|
|
|
1905
2340
|
* Runs the full bitmap height so the below-pane gridline tail stubs melt
|
|
1906
2341
|
* at the edges in step with their lines above.
|
|
1907
2342
|
*/
|
|
1908
|
-
|
|
1909
|
-
const
|
|
1910
|
-
if (
|
|
1911
|
-
const { context:
|
|
1912
|
-
|
|
2343
|
+
#$(t, e, i) {
|
|
2344
|
+
const s = this.#rt(t, e, i);
|
|
2345
|
+
if (s === null) return;
|
|
2346
|
+
const { context: r } = t, n = Math.min(s.inner, s.outer), a = Math.abs(s.outer - s.inner);
|
|
2347
|
+
r.save(), r.globalCompositeOperation = "destination-out", r.fillStyle = this.#Ie({ context: r, side: i, zone: s }), r.fillRect(n, 0, a, t.bitmapSize.height), r.restore();
|
|
1913
2348
|
}
|
|
1914
2349
|
/** Dissolve content leaving through a plain canvas edge — the side opposite
|
|
1915
2350
|
* the Y axis, or either side on a spatial-only chart — when configured. */
|
|
1916
|
-
#
|
|
1917
|
-
const { context:
|
|
1918
|
-
for (const s of this.#
|
|
1919
|
-
const r = this.#
|
|
2351
|
+
#ee(t, e) {
|
|
2352
|
+
const { context: i } = t;
|
|
2353
|
+
for (const s of this.#Ce()) {
|
|
2354
|
+
const r = this.#De({ scope: t, paneBitmapWidth: e, side: s });
|
|
1920
2355
|
if (r === null) continue;
|
|
1921
2356
|
const n = Math.min(r.edge, r.inner), a = Math.abs(r.inner - r.edge);
|
|
1922
|
-
|
|
2357
|
+
i.save(), i.globalCompositeOperation = "destination-out", i.fillStyle = this.#ze({ context: i, side: s, zone: r }), i.fillRect(n, 0, a, t.bitmapSize.height), i.restore();
|
|
1923
2358
|
}
|
|
1924
2359
|
}
|
|
1925
|
-
/** Sides whose fade is a plain edge zone rather than the axis-column ramp
|
|
1926
|
-
|
|
1927
|
-
|
|
2360
|
+
/** Sides whose fade is a plain edge zone rather than the axis-column ramp —
|
|
2361
|
+
* neither the default axis's side nor one a named scale's column holds. */
|
|
2362
|
+
#Ce() {
|
|
2363
|
+
if (this.#Q()) return ee;
|
|
2364
|
+
const t = q(this.yAxisPosition);
|
|
2365
|
+
return this.getAxisColumnWidth(t) > 0 ? se : t === "left" ? re : ne;
|
|
1928
2366
|
}
|
|
1929
2367
|
/** Bitmap px the gridlines and crosshair run under a left Y-axis column, where
|
|
1930
|
-
* data can't follow (see {@link #axisFadeZone}). `0`
|
|
1931
|
-
#
|
|
1932
|
-
return this
|
|
2368
|
+
* data can't follow (see {@link #axisFadeZone}). `0` without one or with its fade off. */
|
|
2369
|
+
#ie(t, e) {
|
|
2370
|
+
return this.#rt(t, e, "left") === null ? 0 : Math.min(Math.round(K * t.horizontalPixelRatio), this.#W(t));
|
|
1933
2371
|
}
|
|
1934
2372
|
/** Left-axis grid pass, run after the axis fade: `destination-over` keeps the
|
|
1935
2373
|
* gridlines out of the data ramp, then they taper out under the column. */
|
|
1936
|
-
#
|
|
1937
|
-
const { grid:
|
|
1938
|
-
n.save(), n.globalCompositeOperation = "destination-over"
|
|
2374
|
+
#Le(t) {
|
|
2375
|
+
const { grid: e, pane: i, overhang: s } = t, { scope: r } = e, { context: n } = r;
|
|
2376
|
+
n.save(), n.globalCompositeOperation = "destination-over";
|
|
2377
|
+
const a = this.#rt(r, i.width, "right")?.intrusion ?? 0;
|
|
2378
|
+
n.save(), n.translate(i.left, 0), n.beginPath(), n.rect(-s, 0, i.width + s + a, i.height), n.clip(), X({ ...e, leftOverhang: s }), n.restore(), this.#Qt({ scope: r, pane: i, timeTicks: e.timeTicks, alpha: e.alpha ?? 1 }), n.restore(), this.#se(r, s);
|
|
1939
2379
|
}
|
|
1940
2380
|
/** Erase the {@link #gridOverhang} strip: none at the pane edge, total at its outer end. */
|
|
1941
|
-
#
|
|
1942
|
-
const
|
|
1943
|
-
r.save(), r.globalCompositeOperation = "destination-out", r.fillStyle = this.#
|
|
2381
|
+
#se(t, e) {
|
|
2382
|
+
const i = this.#W(t), s = i - e, { context: r } = t;
|
|
2383
|
+
r.save(), r.globalCompositeOperation = "destination-out", r.fillStyle = this.#Ve({ context: r, start: s, end: i }), r.fillRect(s, 0, e, t.bitmapSize.height), r.restore();
|
|
1944
2384
|
}
|
|
1945
2385
|
/** Canvas bitmap x of the pane's left edge — whole device pixels, so the
|
|
1946
2386
|
* translated pane keeps crisp strokes. */
|
|
1947
|
-
#
|
|
2387
|
+
#W(t) {
|
|
1948
2388
|
return Math.round(this.plotLeft * t.horizontalPixelRatio);
|
|
1949
2389
|
}
|
|
1950
|
-
/** Pane-local width of the pane clip: the pane plus the
|
|
2390
|
+
/** Pane-local width of the pane clip: the pane plus the right fade
|
|
1951
2391
|
* intrusion, so content can slide under a right Y-axis column before the
|
|
1952
2392
|
* mask dissolves it. */
|
|
1953
|
-
#
|
|
1954
|
-
const
|
|
1955
|
-
return
|
|
2393
|
+
#re(t, e) {
|
|
2394
|
+
const i = this.#rt(t, e, "right")?.intrusion ?? 0;
|
|
2395
|
+
return e + i;
|
|
1956
2396
|
}
|
|
1957
2397
|
/**
|
|
1958
|
-
* Bitmap geometry of the fade zone
|
|
2398
|
+
* Bitmap geometry of the fade zone under the Y-axis column on `side`. On the right the
|
|
1959
2399
|
* ramp finishes just inside the column — {@link AXIS_FADE_END_GAP_PX} past
|
|
1960
2400
|
* the pane edge, still short of where the label glyphs start — so content
|
|
1961
2401
|
* is fully erased before it can cross any axis text; everything wider
|
|
1962
2402
|
* spills back into the pane as the soft lead-in. On the left it finishes
|
|
1963
2403
|
* at the pane edge. Erasing starts at `inner` and is total at `outer`;
|
|
1964
2404
|
* `intrusion` is what the pane clip extends by (content past the ramp is
|
|
1965
|
-
* total-erased anyway). `null` when
|
|
2405
|
+
* total-erased anyway). `null` when no column sits on `side`, its fade
|
|
1966
2406
|
* is `0`, or the chart is spatial-only.
|
|
1967
2407
|
*/
|
|
1968
|
-
#
|
|
1969
|
-
if (this.#
|
|
1970
|
-
const
|
|
1971
|
-
if (
|
|
1972
|
-
const
|
|
2408
|
+
#rt(t, e, i) {
|
|
2409
|
+
if (this.#Q()) return null;
|
|
2410
|
+
const s = i === "left", r = t.bitmapSize.width, n = this.#W(t), a = s ? n : Math.max(0, Math.round(r - n - e));
|
|
2411
|
+
if (a === 0) return null;
|
|
2412
|
+
const o = t.horizontalPixelRatio, h = Math.round(K * o), l = Math.round(te * o), d = s ? 0 : Math.min(h, a), f = s ? h + l : d + l, c = s ? this.#C.left : this.#C.right, m = c === null ? f : Math.round(c * o);
|
|
1973
2413
|
if (m < 1) return null;
|
|
1974
|
-
if (
|
|
1975
|
-
const g =
|
|
1976
|
-
return { inner: g +
|
|
2414
|
+
if (s) {
|
|
2415
|
+
const g = a, S = Math.min(m, r - g);
|
|
2416
|
+
return { inner: g + S, outer: g, intrusion: d };
|
|
1977
2417
|
}
|
|
1978
|
-
const
|
|
1979
|
-
return { inner:
|
|
2418
|
+
const y = n + e + d, u = Math.min(m, y);
|
|
2419
|
+
return { inner: y - u, outer: y, intrusion: d };
|
|
1980
2420
|
}
|
|
1981
2421
|
/** Bitmap geometry of a plain fade at the `side` canvas edge — total erase
|
|
1982
2422
|
* at `edge`, none at `inner`. Off unless that side's fade is configured. */
|
|
1983
|
-
#
|
|
1984
|
-
const { scope:
|
|
2423
|
+
#De(t) {
|
|
2424
|
+
const { scope: e, paneBitmapWidth: i, side: s } = t, r = this.#C[s];
|
|
1985
2425
|
if (r === null) return null;
|
|
1986
|
-
const n = Math.min(Math.round(r *
|
|
2426
|
+
const n = Math.min(Math.round(r * e.horizontalPixelRatio), Math.round(i));
|
|
1987
2427
|
if (n < 1) return null;
|
|
1988
2428
|
if (s === "right") {
|
|
1989
|
-
const a =
|
|
2429
|
+
const a = e.bitmapSize.width;
|
|
1990
2430
|
return { edge: a, inner: a - n };
|
|
1991
2431
|
}
|
|
1992
2432
|
return { edge: 0, inner: n };
|
|
@@ -1995,15 +2435,15 @@ class Ai extends At {
|
|
|
1995
2435
|
* changes (resize, DPR change, `setFade`). Ease-out ramp (`1 - t²`):
|
|
1996
2436
|
* near-total erasure across the upper half keeps a floating header
|
|
1997
2437
|
* legible, then a soft tail into full opacity at the zone's bottom. */
|
|
1998
|
-
#
|
|
1999
|
-
const
|
|
2000
|
-
if (
|
|
2001
|
-
const s = t.createLinearGradient(0, 0, 0,
|
|
2438
|
+
#Re(t, e) {
|
|
2439
|
+
const i = this.#k.top;
|
|
2440
|
+
if (i !== null && i.height === e) return i.gradient;
|
|
2441
|
+
const s = t.createLinearGradient(0, 0, 0, e);
|
|
2002
2442
|
for (let r = 0; r <= 4; r++) {
|
|
2003
2443
|
const n = r / 4;
|
|
2004
2444
|
s.addColorStop(n, `rgba(0, 0, 0, ${1 - n * n})`);
|
|
2005
2445
|
}
|
|
2006
|
-
return this.#
|
|
2446
|
+
return this.#k.top = { height: e, gradient: s }, s;
|
|
2007
2447
|
}
|
|
2008
2448
|
/** Axis-column mask — erase grows from 0 at the ramp's inner start to
|
|
2009
2449
|
* total at its outer end on an ease-in (`s³`): zero slope *and* zero
|
|
@@ -2011,51 +2451,51 @@ class Ai extends At {
|
|
|
2011
2451
|
* candles at rest and the aggressive erasing all happens deep under the
|
|
2012
2452
|
* axis column. Nine stops keep the piecewise interpolation smooth across
|
|
2013
2453
|
* the zone. */
|
|
2014
|
-
#
|
|
2015
|
-
const { context: i, zone:
|
|
2016
|
-
if (
|
|
2017
|
-
const
|
|
2018
|
-
for (let
|
|
2019
|
-
const
|
|
2020
|
-
|
|
2454
|
+
#Ie(t) {
|
|
2455
|
+
const { context: e, side: i, zone: s } = t, r = this.#k.axis[i];
|
|
2456
|
+
if (r !== null && r.inner === s.inner && r.outer === s.outer) return r.gradient;
|
|
2457
|
+
const n = e.createLinearGradient(s.inner, 0, s.outer, 0);
|
|
2458
|
+
for (let a = 0; a <= 8; a++) {
|
|
2459
|
+
const o = a / 8;
|
|
2460
|
+
n.addColorStop(o, `rgba(0, 0, 0, ${o * o * o})`);
|
|
2021
2461
|
}
|
|
2022
|
-
return this.#
|
|
2462
|
+
return this.#k.axis[i] = { inner: s.inner, outer: s.outer, gradient: n }, n;
|
|
2023
2463
|
}
|
|
2024
2464
|
/** Erase ramp for the gridline tail stubs — nothing erased at the pane
|
|
2025
2465
|
* floor, total at the stub tip, ease-in (`t²`) so a stub holds strength
|
|
2026
2466
|
* briefly before melting away short of the label glyphs. */
|
|
2027
|
-
#
|
|
2028
|
-
const
|
|
2029
|
-
if (
|
|
2030
|
-
const
|
|
2467
|
+
#He(t) {
|
|
2468
|
+
const e = this.#k.tail;
|
|
2469
|
+
if (e !== null && e.start === t.start && e.height === t.height) return e.gradient;
|
|
2470
|
+
const i = t.context.createLinearGradient(0, t.start, 0, t.start + t.height);
|
|
2031
2471
|
for (let s = 0; s <= 4; s++) {
|
|
2032
2472
|
const r = s / 4;
|
|
2033
|
-
|
|
2473
|
+
i.addColorStop(r, `rgba(0, 0, 0, ${r * r})`);
|
|
2034
2474
|
}
|
|
2035
|
-
return this.#
|
|
2475
|
+
return this.#k.tail = { start: t.start, height: t.height, gradient: i }, i;
|
|
2036
2476
|
}
|
|
2037
2477
|
/** Gridline overhang ramp — ease-in (`t²`) like the tail stubs. */
|
|
2038
|
-
#
|
|
2039
|
-
const
|
|
2040
|
-
if (
|
|
2041
|
-
const
|
|
2478
|
+
#Ve(t) {
|
|
2479
|
+
const e = this.#k.overhang;
|
|
2480
|
+
if (e !== null && e.start === t.start && e.end === t.end) return e.gradient;
|
|
2481
|
+
const i = t.context.createLinearGradient(t.end, 0, t.start, 0);
|
|
2042
2482
|
for (let s = 0; s <= 4; s++) {
|
|
2043
2483
|
const r = s / 4;
|
|
2044
|
-
|
|
2484
|
+
i.addColorStop(r, `rgba(0, 0, 0, ${r * r})`);
|
|
2045
2485
|
}
|
|
2046
|
-
return this.#
|
|
2486
|
+
return this.#k.overhang = { start: t.start, end: t.end, gradient: i }, i;
|
|
2047
2487
|
}
|
|
2048
2488
|
/** Opposite-edge mask — mirror of the top ramp: total erase at the
|
|
2049
2489
|
* canvas edge, fully opaque at the zone's inner boundary. */
|
|
2050
|
-
#
|
|
2051
|
-
const { context:
|
|
2490
|
+
#ze(t) {
|
|
2491
|
+
const { context: e, side: i, zone: s } = t, r = this.#k.edge[i];
|
|
2052
2492
|
if (r !== null && r.edge === s.edge && r.inner === s.inner) return r.gradient;
|
|
2053
|
-
const n =
|
|
2493
|
+
const n = e.createLinearGradient(s.edge, 0, s.inner, 0);
|
|
2054
2494
|
for (let a = 0; a <= 4; a++) {
|
|
2055
2495
|
const o = a / 4;
|
|
2056
2496
|
n.addColorStop(o, `rgba(0, 0, 0, ${1 - o * o})`);
|
|
2057
2497
|
}
|
|
2058
|
-
return this.#
|
|
2498
|
+
return this.#k.edge[i] = { edge: s.edge, inner: s.inner, gradient: n }, n;
|
|
2059
2499
|
}
|
|
2060
2500
|
/**
|
|
2061
2501
|
* Paint a full frame: the main layer, then the overlay synchronously on
|
|
@@ -2066,94 +2506,98 @@ class Ai extends At {
|
|
|
2066
2506
|
* `markDirty` calls made *during* `renderOverlay` (pulse, edge spinner)
|
|
2067
2507
|
* land after the consume and re-arm cleanly for the next frame.
|
|
2068
2508
|
*/
|
|
2069
|
-
#
|
|
2070
|
-
this.#
|
|
2509
|
+
#wt(t) {
|
|
2510
|
+
this.#Tt("main", t, () => this.renderMain(t)), this.#c.consume(), this.#Tt("overlay", t, () => this.renderOverlay(t));
|
|
2071
2511
|
}
|
|
2072
2512
|
/** Run a layer's paint, booking duration + draw calls to the perf monitor
|
|
2073
2513
|
* when one is attached — keeps overlay time out of the `main` samples. */
|
|
2074
|
-
#
|
|
2075
|
-
const s = this.#
|
|
2514
|
+
#Tt(t, e, i) {
|
|
2515
|
+
const s = this.#M;
|
|
2076
2516
|
if (!s) {
|
|
2077
|
-
|
|
2517
|
+
i();
|
|
2078
2518
|
return;
|
|
2079
2519
|
}
|
|
2080
2520
|
s.resetDrawCalls(t);
|
|
2081
2521
|
const r = performance.now();
|
|
2082
|
-
|
|
2522
|
+
i(), s.recordFrame(t, performance.now() - r, e ?? r);
|
|
2083
2523
|
}
|
|
2084
2524
|
/** Cheap overlay: crosshair, nearest-point dots, pulse animation, edge indicator. */
|
|
2085
2525
|
renderOverlay(t) {
|
|
2086
|
-
const
|
|
2087
|
-
if (
|
|
2088
|
-
let
|
|
2089
|
-
for (const
|
|
2090
|
-
if (
|
|
2091
|
-
|
|
2526
|
+
const e = this.#l.size;
|
|
2527
|
+
if (e.media.width === 0 || e.media.height === 0) return;
|
|
2528
|
+
let i = !1;
|
|
2529
|
+
for (const l of this.#t)
|
|
2530
|
+
if (l.visible && l.renderer.overlayNeedsAnimation) {
|
|
2531
|
+
i = !0;
|
|
2092
2532
|
break;
|
|
2093
2533
|
}
|
|
2094
|
-
this.#
|
|
2534
|
+
this.#y.some((l) => l.pulse) && (i = !0);
|
|
2095
2535
|
const s = {
|
|
2096
|
-
left: this.#
|
|
2097
|
-
right: this.#
|
|
2536
|
+
left: this.#B.left && this.#A.left === "loading" ? "idle" : this.#A.left,
|
|
2537
|
+
right: this.#B.right && this.#A.right === "loading" ? "idle" : this.#A.right
|
|
2098
2538
|
}, r = {
|
|
2099
|
-
left: this.#
|
|
2100
|
-
right: this.#
|
|
2101
|
-
}, n = r.left !== null || r.right !== null, a = s.left === "loading" || s.right === "loading" || n, o = s.left !== "idle" || s.right !== "idle" || n,
|
|
2102
|
-
if (!(!
|
|
2103
|
-
if (this.#
|
|
2104
|
-
if (!
|
|
2105
|
-
const d = (
|
|
2106
|
-
this.#
|
|
2107
|
-
const c = this.#
|
|
2108
|
-
for (const u of this.#t)
|
|
2109
|
-
u.visible
|
|
2110
|
-
|
|
2539
|
+
left: this.#Zt("left"),
|
|
2540
|
+
right: this.#Zt("right")
|
|
2541
|
+
}, n = r.left !== null || r.right !== null, a = s.left === "loading" || s.right === "loading" || n, o = s.left !== "idle" || s.right !== "idle" || n, h = !!this.#f || i || o || this.#y.length > 0 || this.#b.length > 0;
|
|
2542
|
+
if (!(!h && !this.#Lt)) {
|
|
2543
|
+
if (this.#Lt = h, this.#l.useOverlayLayer((l) => {
|
|
2544
|
+
if (!h) return;
|
|
2545
|
+
const d = (e.media.width - this.#w()) * e.horizontalPixelRatio, f = (e.media.height - this.xAxisHeight) * e.verticalPixelRatio;
|
|
2546
|
+
this.#f && this.#Nt() && this.#Pe({ scope: l, chartBitmapWidth: d, chartBitmapHeight: f }), l.context.save(), l.context.translate(this.#W(l), 0), l.context.beginPath(), l.context.rect(0, 0, d, f), l.context.clip();
|
|
2547
|
+
const c = this.#d, m = { top: c.top, bottom: c.bottom }, y = this.getAnimationState();
|
|
2548
|
+
for (const u of this.#t) {
|
|
2549
|
+
if (!u.visible) continue;
|
|
2550
|
+
const g = this.#D(u);
|
|
2551
|
+
u.renderer.drawOverlay?.({
|
|
2552
|
+
scope: l,
|
|
2111
2553
|
timeScale: this.timeScale,
|
|
2112
|
-
yScale: this.
|
|
2554
|
+
yScale: this.getYScale(g),
|
|
2113
2555
|
theme: this.#n,
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2556
|
+
// Overlays only snap the crosshair dots with it.
|
|
2557
|
+
dataInterval: this.#X(),
|
|
2558
|
+
padding: m,
|
|
2559
|
+
crosshair: this.#f,
|
|
2560
|
+
state: g === void 0 ? y : this.#Wt(y, g),
|
|
2118
2561
|
seriesId: u.id
|
|
2119
2562
|
});
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2563
|
+
}
|
|
2564
|
+
this.#de(l, d, f), this.#le(l, d, f), o && ut({
|
|
2565
|
+
scope: l,
|
|
2566
|
+
chartMediaWidth: e.media.width - this.#w(),
|
|
2567
|
+
chartMediaHeight: e.media.height - this.xAxisHeight,
|
|
2124
2568
|
timeScale: this.timeScale,
|
|
2125
2569
|
theme: this.#n,
|
|
2126
2570
|
edgeStates: s,
|
|
2127
2571
|
edgeExitFades: r,
|
|
2128
|
-
edgeIndicatorFns: this.#
|
|
2129
|
-
resolveBoundary: (u) =>
|
|
2130
|
-
resolveEdgeAnchor: (u,
|
|
2131
|
-
resolveEdgeBarSpacing: () => this.#
|
|
2132
|
-
reportPlaceholders: (u,
|
|
2133
|
-
this.#
|
|
2572
|
+
edgeIndicatorFns: this.#It,
|
|
2573
|
+
resolveBoundary: (u) => mt(u, this.#Dt[u], this.getDataBounds()),
|
|
2574
|
+
resolveEdgeAnchor: (u, g) => this.#ve(u, g),
|
|
2575
|
+
resolveEdgeBarSpacing: () => this.#Se(),
|
|
2576
|
+
reportPlaceholders: (u, g) => {
|
|
2577
|
+
this.#ht[u] = { bars: g, at: performance.now() };
|
|
2134
2578
|
}
|
|
2135
|
-
}),
|
|
2136
|
-
}),
|
|
2137
|
-
const { from:
|
|
2138
|
-
let
|
|
2579
|
+
}), l.context.restore();
|
|
2580
|
+
}), i) {
|
|
2581
|
+
const { from: l, to: d } = this.timeScale.getRange();
|
|
2582
|
+
let f = !1;
|
|
2139
2583
|
for (const c of this.#t)
|
|
2140
|
-
if (!(!c.visible || !c.renderer.overlayNeedsAnimation) && (c.renderer.hasOverlayContentInRange?.(
|
|
2141
|
-
|
|
2584
|
+
if (!(!c.visible || !c.renderer.overlayNeedsAnimation) && (c.renderer.hasOverlayContentInRange?.(l, d) ?? !0)) {
|
|
2585
|
+
f = !0;
|
|
2142
2586
|
break;
|
|
2143
2587
|
}
|
|
2144
|
-
if (!
|
|
2145
|
-
for (const c of this.#
|
|
2146
|
-
if (c.pulse && c.time >=
|
|
2147
|
-
|
|
2588
|
+
if (!f) {
|
|
2589
|
+
for (const c of this.#y)
|
|
2590
|
+
if (c.pulse && c.time >= l && c.time <= d) {
|
|
2591
|
+
f = !0;
|
|
2148
2592
|
break;
|
|
2149
2593
|
}
|
|
2150
2594
|
}
|
|
2151
|
-
|
|
2595
|
+
f && this.#c.markDirty();
|
|
2152
2596
|
}
|
|
2153
|
-
a && this.#
|
|
2597
|
+
a && this.#c.markDirty();
|
|
2154
2598
|
}
|
|
2155
2599
|
}
|
|
2156
2600
|
}
|
|
2157
2601
|
export {
|
|
2158
|
-
|
|
2602
|
+
_e as ChartInstance
|
|
2159
2603
|
};
|