@wick-charts/vue 0.6.2 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ChartContainer.vue.js +43 -37
- package/dist/LineSeries.vue.js +2 -2
- package/dist/composables.js +39 -26
- package/dist/core/dist/axis/dom-labels.js +56 -43
- package/dist/core/dist/chart/options.js +5 -5
- package/dist/core/dist/chart/scale-guards.js +26 -0
- package/dist/core/dist/chart/y-target.js +46 -19
- package/dist/core/dist/chart.js +670 -519
- package/dist/core/dist/components/crosshair.js +8 -8
- package/dist/core/dist/components/grid.js +26 -17
- package/dist/core/dist/components/marker.js +28 -28
- package/dist/core/dist/components/reference-line.js +16 -16
- package/dist/core/dist/interactions/handler.js +3 -2
- package/dist/core/dist/interactions/zoom.js +6 -5
- package/dist/core/dist/navigator/controller.js +38 -35
- package/dist/core/dist/perf/perf-hud.js +3 -3
- package/dist/core/dist/scales/log-scale.js +76 -0
- package/dist/core/dist/scales/y-scale.js +134 -35
- package/dist/core/dist/series/bar-intro.js +11 -11
- package/dist/core/dist/series/bar.js +301 -251
- package/dist/core/dist/series/base-multi-layer.js +100 -76
- package/dist/core/dist/series/candlestick.js +244 -241
- package/dist/core/dist/series/line-intro.js +18 -18
- package/dist/core/dist/series/line.js +428 -390
- package/dist/core/dist/series/scale-stack.js +25 -0
- package/dist/core/dist/tooltip-position.js +5 -5
- package/dist/index.d.ts +246 -31
- package/dist/index.js +59 -57
- package/dist/ui/Crosshair.vue.js +14 -14
- package/dist/ui/HeatmapTooltip.vue.js +6 -6
- package/dist/ui/PieTooltip.vue.js +4 -4
- package/dist/ui/TimeAxis.vue.js +23 -22
- package/dist/ui/Tooltip.vue.js +71 -69
- package/dist/ui/YAxis.vue.js +25 -24
- package/dist/ui/YLabel.vue.js +48 -48
- package/package.json +2 -2
- package/src/ChartContainer.vue +20 -8
- package/src/composables.ts +35 -1
- package/src/index.ts +5 -0
- package/src/ui/Crosshair.vue +2 -2
- package/src/ui/TimeAxis.vue +7 -4
- package/src/ui/Tooltip.vue +4 -1
- package/src/ui/YAxis.vue +5 -3
- package/src/ui/YLabel.vue +3 -3
package/dist/core/dist/chart.js
CHANGED
|
@@ -1,55 +1,56 @@
|
|
|
1
|
-
import { Animator as
|
|
2
|
-
import { AnimationConfig as
|
|
3
|
-
import { easeOutCubic as
|
|
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 {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
1
|
+
import { Animator as K } from "./animation/animator.js";
|
|
2
|
+
import { AnimationConfig as j, DEFAULT_LINE_PULSE as q } from "./animation/config.js";
|
|
3
|
+
import { easeOutCubic as W } from "./animation/easing.js";
|
|
4
|
+
import { prefersReducedMotion as J } from "./animation/reduced-motion.js";
|
|
5
|
+
import { createViewportEngine as Q } from "./animation/viewport-engine.js";
|
|
6
|
+
import { CanvasManager as tt } from "./canvas-manager.js";
|
|
7
|
+
import { resolveEdgeAnchorValue as it, drawEdgeIndicators as et, resolveEdgeBoundary as st } from "./chart/edge-indicators.js";
|
|
8
|
+
import { computeFitToData as rt } from "./chart/fit-to-data.js";
|
|
9
|
+
import { getLastValue as nt, getPreviousClose as at, getStackedLastValue as ot } from "./chart/last-value.js";
|
|
10
|
+
import { resolveFade as O, resolveMaxVisibleBars as ht, resolvePadding as F, resolvePerfOptions as lt, isSameHorizontalPadding as N } from "./chart/options.js";
|
|
11
|
+
import { computePan as ct, computeZoom as dt } from "./chart/pan-zoom-math.js";
|
|
12
|
+
import { warnUnplottableBounds as E, assertStackingFitsScale as M, warnUnplottableValues as ut } from "./chart/scale-guards.js";
|
|
13
|
+
import { StreamingCadence as mt } from "./chart/streaming-cadence.js";
|
|
14
|
+
import { computeStreamingTarget as ft } from "./chart/streaming-target.js";
|
|
15
|
+
import { resolvePaddingTime as B } from "./chart/viewport-padding.js";
|
|
16
|
+
import { fromScaleSpace as gt, computeTargetYRange as pt, resolveYTarget as yt } from "./chart/y-target.js";
|
|
17
|
+
import { renderCrosshair as vt } from "./components/crosshair.js";
|
|
18
|
+
import { renderGrid as $ } from "./components/grid.js";
|
|
19
|
+
import { renderMarker as bt } from "./components/marker.js";
|
|
20
|
+
import { renderReferenceLine as xt } from "./components/reference-line.js";
|
|
21
|
+
import { resolveTimeRegionColors as St, renderTimeRegion as kt } from "./components/time-region.js";
|
|
22
|
+
import { EventEmitter as At } from "./events.js";
|
|
23
|
+
import { InteractionHandler as Tt } from "./interactions/handler.js";
|
|
24
|
+
import { RenderScheduler as Y } from "./render-scheduler.js";
|
|
25
|
+
import { XScale as wt } from "./scales/x-scale.js";
|
|
26
|
+
import { YScale as Lt, isPlottableOn as Mt } from "./scales/y-scale.js";
|
|
27
|
+
import { resolveSeriesDefinition as Dt } from "./series/definition.js";
|
|
28
|
+
import { isTimeSeriesRenderer as b } from "./series/types.js";
|
|
29
|
+
import { catppuccin as Pt } from "./theme/themes/catppuccin.js";
|
|
30
|
+
import { clamp as Rt } from "./utils/math.js";
|
|
31
|
+
import { crispLineWidth as Ct, crispCenterOffset as It } from "./utils/pixel-grid.js";
|
|
32
|
+
import { normalizeTime as x, detectInterval as Ht } from "./utils/time.js";
|
|
33
|
+
const zt = 200, Vt = 1500, Wt = 48, X = 12, D = 9, Ot = ["left", "right"], Ft = ["left"], Nt = ["right"];
|
|
34
|
+
let Et = 0, Bt = 0, $t = 0, Yt = 0;
|
|
35
|
+
function Z(y, t) {
|
|
36
|
+
return y === t ? !0 : !y || !t || y.length !== t.length ? !1 : y.every((i, e) => i === t[e]);
|
|
36
37
|
}
|
|
37
|
-
function
|
|
38
|
-
return
|
|
38
|
+
function Xt(y, t) {
|
|
39
|
+
return y === t ? !0 : !y || !t || y.length !== t.length ? !1 : y.every((i, e) => i === t[e]);
|
|
39
40
|
}
|
|
40
|
-
class
|
|
41
|
+
class Ai extends At {
|
|
41
42
|
/** Canvas lifecycle and DPR-aware sizing. */
|
|
42
43
|
#a;
|
|
43
44
|
/** Schedules main-layer redraws (background, grid, series). */
|
|
44
45
|
#i;
|
|
45
46
|
/** Schedules overlay redraws (crosshair). */
|
|
46
|
-
#
|
|
47
|
+
#o;
|
|
47
48
|
/** Maps time values to horizontal pixel coordinates. */
|
|
48
49
|
timeScale;
|
|
49
50
|
/** Maps price/value to vertical pixel coordinates. */
|
|
50
51
|
yScale;
|
|
51
52
|
/** Zoom, pan, crosshair — null when interactive=false. */
|
|
52
|
-
#
|
|
53
|
+
#D;
|
|
53
54
|
/** All registered series (candlestick, line, bar, pie). */
|
|
54
55
|
#t = [];
|
|
55
56
|
/** Point annotations (event markers) drawn on the overlay layer, kept out of the series model. */
|
|
@@ -66,8 +67,8 @@ class ci extends ft {
|
|
|
66
67
|
* later — which re-arms `getIntroFront()`'s global min — can't blink settled
|
|
67
68
|
* markers / reference lines back out.
|
|
68
69
|
*/
|
|
69
|
-
#
|
|
70
|
-
#
|
|
70
|
+
#mt = !1;
|
|
71
|
+
#ft = !1;
|
|
71
72
|
/** Active visual theme (colors, fonts, grid style). */
|
|
72
73
|
#n;
|
|
73
74
|
/** Whether to render the background grid. */
|
|
@@ -78,13 +79,15 @@ class ci extends ft {
|
|
|
78
79
|
* per-tick fades stay snapped during mount (see the tracker's arming), so
|
|
79
80
|
* this is what keeps `fitToData` tick churn from flickering the reveal.
|
|
80
81
|
*/
|
|
81
|
-
#
|
|
82
|
-
/** Resolved edge-fade zones — `
|
|
83
|
-
#
|
|
82
|
+
#H;
|
|
83
|
+
/** Resolved edge-fade zones — a `null` side is auto: the axis-column ramp on the Y-axis side, off on the other. */
|
|
84
|
+
#A;
|
|
85
|
+
/** See {@link headerHeight}. */
|
|
86
|
+
#U = 0;
|
|
84
87
|
/** Memoized destination-out mask gradients, one per edge, keyed by the
|
|
85
88
|
* zone's bitmap geometry so a resize / DPR change / `setFade` rebuilds
|
|
86
89
|
* them and steady frames reuse them. */
|
|
87
|
-
#
|
|
90
|
+
#v = { top: null, axis: null, edge: { left: null, right: null }, tail: null, overhang: null };
|
|
88
91
|
/** Detected time interval between data points (milliseconds). */
|
|
89
92
|
#r = 6e4;
|
|
90
93
|
/** Current crosshair position, null when cursor is outside the chart. */
|
|
@@ -96,45 +99,45 @@ class ci extends ft {
|
|
|
96
99
|
* `time`/`y`; `'time'` (programmatic {@link setCrosshair}) keeps
|
|
97
100
|
* `time`/`y` and re-projects the pixel position.
|
|
98
101
|
*/
|
|
99
|
-
|
|
102
|
+
#K = "pointer";
|
|
100
103
|
/** Whether the last overlay pass painted anything — gates the full-bitmap
|
|
101
104
|
* clear so an empty overlay isn't cleared again every main frame. */
|
|
102
|
-
#
|
|
105
|
+
#gt = !1;
|
|
103
106
|
/** User-specified Y-axis bounds (auto, fixed, percentage). */
|
|
104
|
-
#
|
|
107
|
+
#p = {};
|
|
105
108
|
/** Cached series ID list — invalidated on add/remove. */
|
|
106
|
-
#
|
|
109
|
+
#P = null;
|
|
107
110
|
/** Axis visibility and sizing configuration. */
|
|
108
|
-
#
|
|
111
|
+
#w = {};
|
|
109
112
|
/** Host-declared state per edge — drives the edge-indicator overlay. */
|
|
110
|
-
#
|
|
113
|
+
#S = { left: "idle", right: "idle" };
|
|
111
114
|
/** Wall-clock start of a `loading` exit fade per side — set when the host
|
|
112
115
|
* drops `loading` back to `idle`/`has-more`, so the indicator fades out
|
|
113
116
|
* over {@link EDGE_EXIT_FADE_MS} instead of vanishing the same frame the
|
|
114
117
|
* fetched data lands. `null` = no fade in flight. */
|
|
115
118
|
#N = { left: null, right: null };
|
|
116
119
|
/** Cached boundary timestamps from the last `edgeReached` emission, by side. */
|
|
117
|
-
#
|
|
120
|
+
#pt = { left: null, right: null };
|
|
118
121
|
/** Latest placeholder shapes a `loading` indicator reported per side —
|
|
119
122
|
* the morph hand-off candidate consumed by {@link prependData}. */
|
|
120
|
-
#
|
|
123
|
+
#j = { left: null, right: null };
|
|
121
124
|
/** True after a prepend consumed a side's placeholder snapshot: the series'
|
|
122
125
|
* reveal owns those pixels now, so the still-`loading` indicator is hidden
|
|
123
126
|
* and the upcoming `loading → idle` transition skips its exit fade. Reset
|
|
124
127
|
* by the next {@link setEdgeState} call. */
|
|
125
|
-
#
|
|
128
|
+
#z = { left: !1, right: !1 };
|
|
126
129
|
/** Host-supplied callback fired when the user releases a pan/zoom past a data edge. */
|
|
127
|
-
#
|
|
130
|
+
#yt;
|
|
128
131
|
/** Per-side custom painter for the edge `loading` spinner. `null` falls back to the built-in dots. */
|
|
129
|
-
#
|
|
132
|
+
#vt = { left: null, right: null };
|
|
130
133
|
/** Nesting depth for batch updates. Suppresses recomputes while > 0. */
|
|
131
134
|
#L = 0;
|
|
132
135
|
/** True when batched operations include data changes (triggers full onDataChanged on end). */
|
|
133
|
-
#
|
|
136
|
+
#q = !1;
|
|
134
137
|
/** True when batched operations include visibility changes (triggers Y-range + redraw on end). */
|
|
135
|
-
#
|
|
138
|
+
#V = !1;
|
|
136
139
|
/** True when batched operations bumped overlay version (emits a single overlayChange on flush). */
|
|
137
|
-
#
|
|
140
|
+
#J = !1;
|
|
138
141
|
/**
|
|
139
142
|
* Monotonic counter bumped on any mutation that affects overlay output —
|
|
140
143
|
* data, visibility, series options, theme. Used by snapshot helpers in
|
|
@@ -144,17 +147,36 @@ class ci extends ft {
|
|
|
144
147
|
*
|
|
145
148
|
* @internal
|
|
146
149
|
*/
|
|
147
|
-
#
|
|
150
|
+
#bt = 0;
|
|
151
|
+
/** CSS px of the Y-axis column. The default collapses to `0` on a spatial-only chart. */
|
|
148
152
|
get yAxisWidth() {
|
|
149
|
-
const t = this.#
|
|
150
|
-
return t?.visible === !1 ? 0 : t?.width
|
|
153
|
+
const t = this.#w.y;
|
|
154
|
+
return t?.visible === !1 ? 0 : t?.width !== void 0 ? t.width : this.#X() ? 0 : 55;
|
|
155
|
+
}
|
|
156
|
+
/** Side of the plot area the Y-axis column sits on. */
|
|
157
|
+
get yAxisPosition() {
|
|
158
|
+
return this.#w.y?.position ?? "right";
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Left edge of the plot area in container CSS px — the Y-axis width when
|
|
162
|
+
* the axis sits on the left, `0` otherwise. Scales map into the plot area,
|
|
163
|
+
* so add this to `timeScale.timeToX()` to get a container coordinate.
|
|
164
|
+
*/
|
|
165
|
+
get plotLeft() {
|
|
166
|
+
return this.yAxisPosition === "left" ? this.yAxisWidth : 0;
|
|
151
167
|
}
|
|
168
|
+
/** CSS px height of the floating Title / InfoBar header, `0` without one. A left
|
|
169
|
+
* axis shares its column with it, so tick labels fade out underneath. */
|
|
170
|
+
get headerHeight() {
|
|
171
|
+
return this.#U;
|
|
172
|
+
}
|
|
173
|
+
/** CSS px of the time-axis row. The default collapses to `0` on a spatial-only chart. */
|
|
152
174
|
get xAxisHeight() {
|
|
153
|
-
const t = this.#
|
|
154
|
-
return t?.visible === !1 ? 0 : t?.height
|
|
175
|
+
const t = this.#w.x;
|
|
176
|
+
return t?.visible === !1 ? 0 : t?.height !== void 0 ? t.height : this.#X() ? 0 : 30;
|
|
155
177
|
}
|
|
156
178
|
/** Resolved animation config derived from `options.animations` at construction. */
|
|
157
|
-
#
|
|
179
|
+
#d;
|
|
158
180
|
/**
|
|
159
181
|
* Single source of truth for the chart's X / Y viewport animation.
|
|
160
182
|
* Push-model API: chart emits intent signals (`onPointAppended`,
|
|
@@ -179,9 +201,9 @@ class ci extends ft {
|
|
|
179
201
|
* field carries the decision across that gap. The closure also reads
|
|
180
202
|
* `#dataStart` / `#dataEnd` directly for the timestamps.
|
|
181
203
|
*/
|
|
182
|
-
#
|
|
204
|
+
#xt = !1;
|
|
183
205
|
/** Earliest data timestamp registered across all series, `null` before any data has arrived. */
|
|
184
|
-
#
|
|
206
|
+
#T = null;
|
|
185
207
|
/** Latest data timestamp registered across all series, `null` before any data has arrived. */
|
|
186
208
|
#e = null;
|
|
187
209
|
/**
|
|
@@ -190,51 +212,51 @@ class ci extends ft {
|
|
|
190
212
|
* left of the tail keeps that offset as new bars arrive, instead of
|
|
191
213
|
* snapping back to the natural-pin position every frame.
|
|
192
214
|
*/
|
|
193
|
-
#
|
|
215
|
+
#E = null;
|
|
194
216
|
/**
|
|
195
217
|
* Warm-up window flag — `setVisibleRange({ from, bars })` arms it and
|
|
196
218
|
* the streaming-target math suppresses pan ticks while the data hasn't
|
|
197
219
|
* reached the right edge yet. Cleared by user interaction or once the
|
|
198
220
|
* data fills the window.
|
|
199
221
|
*/
|
|
200
|
-
#
|
|
222
|
+
#M = !1;
|
|
201
223
|
/**
|
|
202
224
|
* Cap for fit-to-data. When the data span exceeds this, the fit anchors
|
|
203
225
|
* the right edge and trims the left. Resolved once at construction from
|
|
204
226
|
* `options.viewport.maxVisibleBars`.
|
|
205
227
|
*/
|
|
206
|
-
#
|
|
228
|
+
#St;
|
|
207
229
|
/**
|
|
208
230
|
* Committed logical X range — the "where X should be" target. Engine
|
|
209
231
|
* eases its visual toward this. Pan / zoom / setRange / fitContent /
|
|
210
232
|
* streaming-target writes go here. Initialized to `{0,0}` and treated
|
|
211
233
|
* as "uninitialized" by `#computeXTarget` until first paint.
|
|
212
234
|
*/
|
|
213
|
-
#
|
|
235
|
+
#h = { from: 0, to: 0 };
|
|
214
236
|
/** Padded Y range — engine's raw Y target plus pixel padding from `#padding`. */
|
|
215
|
-
#
|
|
237
|
+
#B = { min: 0, max: 0 };
|
|
216
238
|
/**
|
|
217
239
|
* Auto-scroll flag — when `true`, streaming ticks slide X to track the
|
|
218
240
|
* data tail. Pan flips this off when the gesture pushes the tail off
|
|
219
241
|
* screen; chart's per-frame autoscroll check re-engages it when a
|
|
220
242
|
* follow-up pan brings the tail back into view.
|
|
221
243
|
*/
|
|
222
|
-
#
|
|
244
|
+
#m = !0;
|
|
223
245
|
/** Resolved viewport padding (top/bottom px + left/right pixels-or-intervals). */
|
|
224
246
|
#l;
|
|
225
247
|
/** Cached chart width — passed by interactions on every call; kept for the rare
|
|
226
248
|
* call that omits it (e.g. programmatic `chart.pan(...)` without explicit width). */
|
|
227
|
-
|
|
249
|
+
#$ = 0;
|
|
228
250
|
/** Active performance monitor, or `null` when instrumentation is disabled (the default). */
|
|
229
|
-
#
|
|
251
|
+
#k;
|
|
230
252
|
/** When true, `destroy()` tears down the monitor; false for caller-supplied monitors we must not destroy. */
|
|
231
|
-
#
|
|
253
|
+
#Q = !1;
|
|
232
254
|
/** Visible HUD overlay (mounted by the perf config); non-null only when the caller requested one. */
|
|
233
|
-
#
|
|
255
|
+
#tt = null;
|
|
234
256
|
constructor(t, i) {
|
|
235
|
-
super(), i?.axis && (this.#
|
|
236
|
-
const e = this.#
|
|
237
|
-
this.#s =
|
|
257
|
+
super(), i?.axis && (this.#w = i.axis, this.#p = { min: i.axis.y?.min, max: i.axis.y?.max }), this.#n = i?.theme ?? Pt.theme, this.#F = i?.grid?.visible !== !1, this.#A = O(i?.fade), this.#d = j.resolve(i?.animations), this.#St = ht(i?.viewport?.maxVisibleBars), this.#l = F(i?.padding), this.#R = new mt();
|
|
258
|
+
const e = this.#d.axis.y, s = this.#d.axis.x;
|
|
259
|
+
this.#s = Q({
|
|
238
260
|
initial: { yRange: { min: 0, max: 0 }, xRange: { from: 0, to: 0 } },
|
|
239
261
|
y: {
|
|
240
262
|
curve: e.curve,
|
|
@@ -242,36 +264,36 @@ class ci extends ft {
|
|
|
242
264
|
stickyMs: e.stickyMs,
|
|
243
265
|
stickyFloorMs: e.stickyFloorMs,
|
|
244
266
|
gestureMs: e.gestureMs,
|
|
245
|
-
toggleMs: this.#
|
|
267
|
+
toggleMs: this.#d.toggleMs
|
|
246
268
|
},
|
|
247
269
|
x: {
|
|
248
270
|
curve: s.curve,
|
|
249
271
|
settleMs: s.settleMs,
|
|
250
272
|
gestureMs: s.gestureMs
|
|
251
273
|
},
|
|
252
|
-
computeXTarget: () => this.#
|
|
253
|
-
computeYTarget: ({ xTarget:
|
|
274
|
+
computeXTarget: () => this.#Jt(),
|
|
275
|
+
computeYTarget: ({ xTarget: o }) => this.#ht(o),
|
|
254
276
|
onWake: () => this.#i?.markDirty()
|
|
255
|
-
}), this.#
|
|
256
|
-
const r =
|
|
257
|
-
this.#
|
|
258
|
-
const n = this.#
|
|
259
|
-
this.timeScale.tickTracker.setFadeMs(n), this.yScale.tickTracker.setFadeMs(n), this.#
|
|
277
|
+
}), this.#yt = i?.onEdgeReached, this.#_ = i?.viewport?.initialRange;
|
|
278
|
+
const r = lt(i?.perf);
|
|
279
|
+
this.#k = r.monitor, this.#Q = r.ownsMonitor, this.#a = new tt(t, this.#k ?? void 0), this.timeScale = new wt(), this.yScale = new Lt(), this.yScale.setTransform(this.#w.y?.scale ?? null), E({ y: this.#w.y, isPlottable: this.#Y }), this.timeScale.setLocale(i?.locale), this.timeScale.setTimeZone(i?.timeZone);
|
|
280
|
+
const n = this.#d.axis.ticksMs;
|
|
281
|
+
this.timeScale.tickTracker.setFadeMs(n), this.yScale.tickTracker.setFadeMs(n), this.#H = new K({
|
|
260
282
|
initial: 0,
|
|
261
|
-
duration: this.#
|
|
262
|
-
lerp: (
|
|
263
|
-
}), this.#i = new
|
|
264
|
-
(
|
|
265
|
-
), this.#
|
|
283
|
+
duration: this.#d.axis.gridMs,
|
|
284
|
+
lerp: (o, l, h) => o + (l - o) * h
|
|
285
|
+
}), this.#i = new Y((o) => this.#dt(o)), this.#o = new Y(
|
|
286
|
+
(o) => this.#ut("overlay", o, () => this.renderOverlay(o))
|
|
287
|
+
), this.#k && r.hud && (this.#tt = r.hud(t, this.#k));
|
|
266
288
|
const a = i?.interactive !== !1;
|
|
267
|
-
this.#
|
|
268
|
-
this.syncScales(), this.#
|
|
269
|
-
}), this.syncScales(), this.#
|
|
270
|
-
this
|
|
271
|
-
}), this.#
|
|
272
|
-
this.emit("pointClick", { ...
|
|
273
|
-
}), this.#
|
|
274
|
-
this.fitContent(), this.emit("pointDoubleClick", { ...
|
|
289
|
+
this.#D = a ? new Tt(this.#a.canvas, this, this.timeScale, this.yScale) : null, this.#a.on("resize", () => {
|
|
290
|
+
this.syncScales(), this.#dt(), this.emit("viewportChange");
|
|
291
|
+
}), this.syncScales(), this.#D?.on("crosshairMove", (o) => {
|
|
292
|
+
this.#K = "pointer", this.#c = o, this.#o.markDirty(), this.emit("crosshairMove", o), this.#rt(o);
|
|
293
|
+
}), this.#D?.on("click", (o) => {
|
|
294
|
+
this.emit("pointClick", { ...o, spatialHit: this.#Pt(o) });
|
|
295
|
+
}), this.#D?.on("dblclick", (o) => {
|
|
296
|
+
this.fitContent(), this.emit("pointDoubleClick", { ...o, spatialHit: this.#Pt(o) });
|
|
275
297
|
});
|
|
276
298
|
}
|
|
277
299
|
/**
|
|
@@ -286,9 +308,9 @@ class ci extends ft {
|
|
|
286
308
|
* `setSeriesVisible` / `setLayerVisible` calls — produce one overlay
|
|
287
309
|
* render instead of N.
|
|
288
310
|
*/
|
|
289
|
-
#
|
|
290
|
-
if (this.#
|
|
291
|
-
this.#
|
|
311
|
+
#f() {
|
|
312
|
+
if (this.#bt++, this.#L > 0) {
|
|
313
|
+
this.#J = !0;
|
|
292
314
|
return;
|
|
293
315
|
}
|
|
294
316
|
this.emit("overlayChange");
|
|
@@ -302,39 +324,46 @@ class ci extends ft {
|
|
|
302
324
|
* @internal
|
|
303
325
|
*/
|
|
304
326
|
getOverlayVersion() {
|
|
305
|
-
return this.#
|
|
327
|
+
return this.#bt;
|
|
306
328
|
}
|
|
307
329
|
/**
|
|
308
330
|
* Return a series ID: use the provided hint if it's non-empty and not already taken,
|
|
309
331
|
* otherwise generate a new auto ID. Auto-generated IDs never collide with each other
|
|
310
332
|
* or with user-provided IDs because they use a monotonically increasing counter.
|
|
311
333
|
*/
|
|
312
|
-
|
|
334
|
+
#$t(t) {
|
|
313
335
|
if (t && !this.#t.some((e) => e.id === t))
|
|
314
336
|
return t;
|
|
315
337
|
let i;
|
|
316
338
|
do
|
|
317
|
-
i = `series_${++
|
|
339
|
+
i = `series_${++Et}`;
|
|
318
340
|
while (this.#t.some((e) => e.id === i));
|
|
319
341
|
return i;
|
|
320
342
|
}
|
|
321
343
|
addSeries(t, i = {}) {
|
|
322
|
-
const e = typeof t == "string" ?
|
|
344
|
+
const e = typeof t == "string" ? Dt(t) : t;
|
|
323
345
|
if (!e)
|
|
324
346
|
throw new Error(
|
|
325
347
|
`[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).`
|
|
326
348
|
);
|
|
327
|
-
const { layers: s, id: r, label: n, labels: a, ...
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
...this.#
|
|
331
|
-
|
|
332
|
-
|
|
349
|
+
const { layers: s, id: r, label: n, labels: a, ...o } = i, l = s ?? 1;
|
|
350
|
+
M({ seriesId: r ?? e.type, stacking: o.stacking, isPlottable: this.#Y });
|
|
351
|
+
const h = {
|
|
352
|
+
...this.#d.defaults(e.type),
|
|
353
|
+
...o,
|
|
354
|
+
...this.#d.overrides(e.type)
|
|
355
|
+
}, d = e.create({ theme: this.#n, layerCount: l }, h), m = a ?? (typeof n == "string" ? [n] : void 0), c = this.#$t(r);
|
|
356
|
+
d.onDataChanged?.(() => this.onDataChanged());
|
|
357
|
+
const f = this.#Z();
|
|
358
|
+
return this.#t.push({ id: c, labels: m, renderer: d, visible: !0 }), this.#P = null, this.#st(), this.#et(f), this.emit("seriesChange"), this.#f(), c;
|
|
333
359
|
}
|
|
334
360
|
/** Remove a series by ID and clean up its resources. */
|
|
335
361
|
removeSeries(t) {
|
|
336
362
|
const i = this.#t.findIndex((e) => e.id === t);
|
|
337
|
-
i >= 0
|
|
363
|
+
if (i >= 0) {
|
|
364
|
+
const e = this.#Z();
|
|
365
|
+
this.#t[i].renderer.dispose(), this.#t.splice(i, 1), this.#P = null, this.#st(), this.#et(e), this.#i.markDirty(), this.emit("seriesChange"), this.#f();
|
|
366
|
+
}
|
|
338
367
|
}
|
|
339
368
|
// --- Markers (point annotations) -----------------------------------------
|
|
340
369
|
/**
|
|
@@ -347,24 +376,24 @@ class ci extends ft {
|
|
|
347
376
|
* appear in series queries.
|
|
348
377
|
*/
|
|
349
378
|
addMarker(t) {
|
|
350
|
-
const i = `marker_${++
|
|
351
|
-
return this.#u.push(this.#
|
|
379
|
+
const i = `marker_${++Bt}`;
|
|
380
|
+
return this.#u.push(this.#kt(i, t)), this.#o.markDirty(), i;
|
|
352
381
|
}
|
|
353
382
|
/** Replace a marker's config in place. No-op when the id is unknown. */
|
|
354
383
|
updateMarker(t, i) {
|
|
355
384
|
const e = this.#u.findIndex((s) => s.id === t);
|
|
356
|
-
e !== -1 && (this.#u[e] = this.#
|
|
385
|
+
e !== -1 && (this.#u[e] = this.#kt(t, i), this.#o.markDirty());
|
|
357
386
|
}
|
|
358
387
|
/** Remove a marker by id. No-op when the id is unknown. */
|
|
359
388
|
removeMarker(t) {
|
|
360
389
|
const i = this.#u.findIndex((e) => e.id === t);
|
|
361
|
-
i !== -1 && (this.#u.splice(i, 1), this.#
|
|
390
|
+
i !== -1 && (this.#u.splice(i, 1), this.#o.markDirty());
|
|
362
391
|
}
|
|
363
392
|
/** Read-only snapshot of the registered markers (normalized). Mutate via add / update / removeMarker. */
|
|
364
393
|
getMarkers() {
|
|
365
394
|
return this.#u;
|
|
366
395
|
}
|
|
367
|
-
#
|
|
396
|
+
#kt(t, i) {
|
|
368
397
|
return {
|
|
369
398
|
id: t,
|
|
370
399
|
time: x(i.time),
|
|
@@ -377,14 +406,14 @@ class ci extends ft {
|
|
|
377
406
|
};
|
|
378
407
|
}
|
|
379
408
|
/** Resolve a marker's Y value: explicit `value`, else snapped to its series at `time`. Null when neither resolves. */
|
|
380
|
-
#
|
|
409
|
+
#Yt(t) {
|
|
381
410
|
if (t.value !== void 0 && Number.isFinite(t.value)) return t.value;
|
|
382
411
|
if (t.seriesId === void 0) return null;
|
|
383
412
|
const i = this.getDataAtTime(t.seriesId, t.time);
|
|
384
413
|
return i ? "close" in i ? i.close : i.value : null;
|
|
385
414
|
}
|
|
386
415
|
/** Resolve a marker's color: explicit `color`, else the series color, else the theme line color. */
|
|
387
|
-
#
|
|
416
|
+
#Xt(t) {
|
|
388
417
|
if (t.color !== void 0) return t.color;
|
|
389
418
|
if (t.seriesId !== void 0) {
|
|
390
419
|
const i = this.getSeriesColor(t.seriesId);
|
|
@@ -392,16 +421,16 @@ class ci extends ft {
|
|
|
392
421
|
}
|
|
393
422
|
return this.#n.line.color;
|
|
394
423
|
}
|
|
395
|
-
#
|
|
424
|
+
#Zt(t, i, e) {
|
|
396
425
|
if (this.#u.length === 0) return;
|
|
397
|
-
const s = performance.now() / (
|
|
426
|
+
const s = performance.now() / (q * 2 * Math.PI) % 1, r = this.#it();
|
|
398
427
|
for (const n of this.#u) {
|
|
399
|
-
const a = this.#
|
|
428
|
+
const a = this.#Yt(n);
|
|
400
429
|
if (a === null) continue;
|
|
401
|
-
const
|
|
402
|
-
if (
|
|
430
|
+
const o = this.#Lt(r, n.time);
|
|
431
|
+
if (o <= 0) continue;
|
|
403
432
|
const l = t.context.globalAlpha;
|
|
404
|
-
|
|
433
|
+
o < 1 && (t.context.globalAlpha = l * o), bt({
|
|
405
434
|
scope: t,
|
|
406
435
|
timeScale: this.timeScale,
|
|
407
436
|
yScale: this.yScale,
|
|
@@ -409,7 +438,7 @@ class ci extends ft {
|
|
|
409
438
|
marker: {
|
|
410
439
|
time: n.time,
|
|
411
440
|
value: a,
|
|
412
|
-
color: this.#
|
|
441
|
+
color: this.#Xt(n),
|
|
413
442
|
shape: n.shape,
|
|
414
443
|
pulse: n.pulse,
|
|
415
444
|
label: n.label
|
|
@@ -417,7 +446,7 @@ class ci extends ft {
|
|
|
417
446
|
phase: s,
|
|
418
447
|
plotWidth: i,
|
|
419
448
|
plotHeight: e
|
|
420
|
-
}),
|
|
449
|
+
}), o < 1 && (t.context.globalAlpha = l);
|
|
421
450
|
}
|
|
422
451
|
}
|
|
423
452
|
// --- Time regions (shaded bands) -----------------------------------------
|
|
@@ -433,13 +462,13 @@ class ci extends ft {
|
|
|
433
462
|
* legend.
|
|
434
463
|
*/
|
|
435
464
|
addRegion(t) {
|
|
436
|
-
const i = `region_${
|
|
437
|
-
return this.#x.push(this.#
|
|
465
|
+
const i = `region_${++$t}`;
|
|
466
|
+
return this.#x.push(this.#At(i, t)), this.#i.markDirty(), i;
|
|
438
467
|
}
|
|
439
468
|
/** Replace a region's config in place. No-op when the id is unknown. */
|
|
440
469
|
updateRegion(t, i) {
|
|
441
470
|
const e = this.#x.findIndex((s) => s.id === t);
|
|
442
|
-
e !== -1 && (this.#x[e] = this.#
|
|
471
|
+
e !== -1 && (this.#x[e] = this.#At(t, i), this.#i.markDirty());
|
|
443
472
|
}
|
|
444
473
|
/** Remove a region by id. No-op when the id is unknown. */
|
|
445
474
|
removeRegion(t) {
|
|
@@ -450,7 +479,7 @@ class ci extends ft {
|
|
|
450
479
|
getRegions() {
|
|
451
480
|
return this.#x;
|
|
452
481
|
}
|
|
453
|
-
#
|
|
482
|
+
#At(t, i) {
|
|
454
483
|
const { to: e } = i, s = e === void 0 || e === "now" ? null : x(e);
|
|
455
484
|
return {
|
|
456
485
|
id: t,
|
|
@@ -461,11 +490,11 @@ class ci extends ft {
|
|
|
461
490
|
label: i.label
|
|
462
491
|
};
|
|
463
492
|
}
|
|
464
|
-
#
|
|
493
|
+
#_t(t, i, e) {
|
|
465
494
|
if (this.#x.length !== 0)
|
|
466
495
|
for (const s of this.#x) {
|
|
467
|
-
const { color: r, fill: n } =
|
|
468
|
-
|
|
496
|
+
const { color: r, fill: n } = St(s, this.#n);
|
|
497
|
+
kt({
|
|
469
498
|
scope: t,
|
|
470
499
|
timeScale: this.timeScale,
|
|
471
500
|
theme: this.#n,
|
|
@@ -487,24 +516,24 @@ class ci extends ft {
|
|
|
487
516
|
* legend.
|
|
488
517
|
*/
|
|
489
518
|
addLine(t) {
|
|
490
|
-
const i = `line_${++
|
|
491
|
-
return this.#y.push(this.#
|
|
519
|
+
const i = `line_${++Yt}`;
|
|
520
|
+
return this.#y.push(this.#Tt(i, t)), this.#o.markDirty(), i;
|
|
492
521
|
}
|
|
493
522
|
/** Replace a reference line's config in place. No-op when the id is unknown. */
|
|
494
523
|
updateLine(t, i) {
|
|
495
524
|
const e = this.#y.findIndex((s) => s.id === t);
|
|
496
|
-
e !== -1 && (this.#y[e] = this.#
|
|
525
|
+
e !== -1 && (this.#y[e] = this.#Tt(t, i), this.#o.markDirty());
|
|
497
526
|
}
|
|
498
527
|
/** Remove a reference line by id. No-op when the id is unknown. */
|
|
499
528
|
removeLine(t) {
|
|
500
529
|
const i = this.#y.findIndex((e) => e.id === t);
|
|
501
|
-
i !== -1 && (this.#y.splice(i, 1), this.#
|
|
530
|
+
i !== -1 && (this.#y.splice(i, 1), this.#o.markDirty());
|
|
502
531
|
}
|
|
503
532
|
/** Read-only snapshot of the registered reference lines (normalized). Mutate via add / update / removeLine. */
|
|
504
533
|
getLines() {
|
|
505
534
|
return this.#y;
|
|
506
535
|
}
|
|
507
|
-
#
|
|
536
|
+
#Tt(t, i) {
|
|
508
537
|
const { time: e, value: s } = i, r = {
|
|
509
538
|
id: t,
|
|
510
539
|
color: i.color,
|
|
@@ -514,14 +543,14 @@ class ci extends ft {
|
|
|
514
543
|
};
|
|
515
544
|
return e !== void 0 ? { ...r, orientation: "vertical", coord: x(e) } : { ...r, orientation: "horizontal", coord: s ?? Number.NaN };
|
|
516
545
|
}
|
|
517
|
-
#
|
|
546
|
+
#Gt(t, i, e) {
|
|
518
547
|
if (this.#y.length === 0) return;
|
|
519
|
-
const s = this.#
|
|
548
|
+
const s = this.#it();
|
|
520
549
|
for (const r of this.#y) {
|
|
521
|
-
const n = s >= 1 ? 1 : r.orientation === "vertical" ? this.#
|
|
550
|
+
const n = s >= 1 ? 1 : r.orientation === "vertical" ? this.#Lt(s, r.coord) : W(s);
|
|
522
551
|
if (n <= 0) continue;
|
|
523
552
|
const a = t.context.globalAlpha;
|
|
524
|
-
n < 1 && (t.context.globalAlpha = a * n),
|
|
553
|
+
n < 1 && (t.context.globalAlpha = a * n), xt({
|
|
525
554
|
scope: t,
|
|
526
555
|
timeScale: this.timeScale,
|
|
527
556
|
yScale: this.yScale,
|
|
@@ -546,8 +575,15 @@ class ci extends ft {
|
|
|
546
575
|
* underlying animator mutates between frames.
|
|
547
576
|
*/
|
|
548
577
|
getAnimationState() {
|
|
549
|
-
return this.#s.getAnimationState();
|
|
578
|
+
return this.#wt(this.#s.getAnimationState());
|
|
579
|
+
}
|
|
580
|
+
/** The engine animates Y in scale space (`axis.y.scale`'s forward); readers outside it get values. */
|
|
581
|
+
#wt(t) {
|
|
582
|
+
const i = this.yScale.getTransform();
|
|
583
|
+
return i === null ? t : { ...t, yRange: gt(t.yRange, i) };
|
|
550
584
|
}
|
|
585
|
+
/** `yScale.isPlottable`, bound once for the helpers that take a predicate. */
|
|
586
|
+
#Y = (t) => this.yScale.isPlottable(t);
|
|
551
587
|
/**
|
|
552
588
|
* Reveal-front position in `[0, 1]` of any in-flight initial-load intro.
|
|
553
589
|
* With a `seriesId`, that series' own front; without, the minimum across
|
|
@@ -574,10 +610,10 @@ class ci extends ft {
|
|
|
574
610
|
* has actually been observed in progress, so an annotation drawn on a chart
|
|
575
611
|
* that has no data yet still rides the reveal once data arrives.
|
|
576
612
|
*/
|
|
577
|
-
#
|
|
578
|
-
if (this.#
|
|
613
|
+
#it() {
|
|
614
|
+
if (this.#ft) return 1;
|
|
579
615
|
const t = this.getIntroFront();
|
|
580
|
-
return t < 1 ? (this.#
|
|
616
|
+
return t < 1 ? (this.#mt = !0, t) : (this.#mt && (this.#ft = !0), 1);
|
|
581
617
|
}
|
|
582
618
|
/**
|
|
583
619
|
* Entrance alpha for an overlay pinned at `time`, given the intro reveal
|
|
@@ -585,10 +621,10 @@ class ci extends ft {
|
|
|
585
621
|
* then fades in over a short band — so annotations surface in step with the
|
|
586
622
|
* series drawing past them. `1` once the intro has settled.
|
|
587
623
|
*/
|
|
588
|
-
#
|
|
624
|
+
#Lt(t, i) {
|
|
589
625
|
if (t >= 1) return 1;
|
|
590
626
|
const { from: e, to: s } = this.timeScale.getRange(), r = s - e, n = r > 0 ? (i - e) / r : 0;
|
|
591
|
-
return
|
|
627
|
+
return Rt((t - n) / 0.12, 0, 1);
|
|
592
628
|
}
|
|
593
629
|
/**
|
|
594
630
|
* Replace all data for a series.
|
|
@@ -601,7 +637,7 @@ class ci extends ft {
|
|
|
601
637
|
*/
|
|
602
638
|
setSeriesData(t, i, e) {
|
|
603
639
|
const s = this.#t.find((r) => r.id === t);
|
|
604
|
-
s && (this.#
|
|
640
|
+
s && (this.#nt = !0, s.renderer.setData(i, e));
|
|
605
641
|
}
|
|
606
642
|
/** Append a new data point to the end of a series (real-time tick). */
|
|
607
643
|
appendData(t, i, e) {
|
|
@@ -624,8 +660,8 @@ class ci extends ft {
|
|
|
624
660
|
prependData(t, i, e) {
|
|
625
661
|
const s = this.#t.find((n) => n.id === t);
|
|
626
662
|
if (s === void 0 || s.renderer.prependPoints === void 0) return;
|
|
627
|
-
const r = this.#
|
|
628
|
-
r !== null && s.renderer.setHistoryHandoff !== void 0 && performance.now() - r.at <
|
|
663
|
+
const r = this.#j.left;
|
|
664
|
+
r !== null && s.renderer.setHistoryHandoff !== void 0 && performance.now() - r.at < Vt && (s.renderer.setHistoryHandoff(r.bars), this.#z.left = !0, this.#N.left = null, this.#j.left = null, this.#o.markDirty()), s.renderer.prependPoints(i, e);
|
|
629
665
|
}
|
|
630
666
|
/** Update the last data point of a series in place (e.g. live candle update). */
|
|
631
667
|
updateData(t, i, e) {
|
|
@@ -649,12 +685,13 @@ class ci extends ft {
|
|
|
649
685
|
}
|
|
650
686
|
/** Update visual options (color, width, etc.) for an existing series. */
|
|
651
687
|
updateSeriesOptions(t, i) {
|
|
652
|
-
const e = this.#t.find((
|
|
688
|
+
const e = this.#t.find((h) => h.id === t);
|
|
653
689
|
if (!e) return;
|
|
654
|
-
|
|
690
|
+
"stacking" in i && M({ seriesId: t, stacking: i.stacking, isPlottable: this.#Y });
|
|
691
|
+
const s = e.labels, r = e.renderer.getLayerColors().slice(), n = this.#d.overrides(e.renderer.kind);
|
|
655
692
|
e.renderer.updateOptions({ ...i, ...n }), "label" in i && typeof i.label == "string" && (e.labels = [i.label]), this.#i.markDirty();
|
|
656
|
-
const a = e.renderer.getLayerColors(),
|
|
657
|
-
(
|
|
693
|
+
const a = e.renderer.getLayerColors(), o = r.length !== a.length || r.some((h, d) => h !== a[d]), l = !Z(s, e.labels);
|
|
694
|
+
(o || l) && this.#f();
|
|
658
695
|
}
|
|
659
696
|
/**
|
|
660
697
|
* Batch multiple updates: suppress recomputes until `fn` returns. Exceptions
|
|
@@ -666,14 +703,14 @@ class ci extends ft {
|
|
|
666
703
|
t();
|
|
667
704
|
} finally {
|
|
668
705
|
if (--this.#L <= 0) {
|
|
669
|
-
if (this.#L = 0, this.#
|
|
670
|
-
this.#
|
|
671
|
-
else if (this.#
|
|
672
|
-
this.#
|
|
706
|
+
if (this.#L = 0, this.#q)
|
|
707
|
+
this.#q = !1, this.#V = !1, this.onDataChanged();
|
|
708
|
+
else if (this.#V) {
|
|
709
|
+
this.#V = !1, this.#g = !0;
|
|
673
710
|
const i = performance.now();
|
|
674
|
-
this.#s.onAxisReconfig(i), this.#
|
|
711
|
+
this.#s.onAxisReconfig(i), this.#b(i), this.#i.markDirty();
|
|
675
712
|
}
|
|
676
|
-
this.#
|
|
713
|
+
this.#J && (this.#J = !1, this.emit("overlayChange"));
|
|
677
714
|
}
|
|
678
715
|
}
|
|
679
716
|
}
|
|
@@ -687,15 +724,15 @@ class ci extends ft {
|
|
|
687
724
|
setSeriesVisible(t, i) {
|
|
688
725
|
const e = this.#t.find((n) => n.id === t);
|
|
689
726
|
if (!e || e.visible === i) return;
|
|
690
|
-
e.visible = i, this.#
|
|
691
|
-
const s = this.#
|
|
727
|
+
e.visible = i, this.#f(), this.#st();
|
|
728
|
+
const s = this.#d.toggleMs;
|
|
692
729
|
if (e.renderer.setAlpha?.(i ? 1 : 0, s), this.#i.markDirty(), this.#L > 0) {
|
|
693
|
-
this.#
|
|
730
|
+
this.#V = !0;
|
|
694
731
|
return;
|
|
695
732
|
}
|
|
696
733
|
this.#g = !0;
|
|
697
734
|
const r = performance.now();
|
|
698
|
-
this.#s.onSeriesVisibilityChanged(r), this.#
|
|
735
|
+
this.#s.onSeriesVisibilityChanged(r), this.#b(r);
|
|
699
736
|
}
|
|
700
737
|
/**
|
|
701
738
|
* Whether streaming ticks slide the visible range to track the data tail.
|
|
@@ -705,7 +742,7 @@ class ci extends ft {
|
|
|
705
742
|
* containing the tail.
|
|
706
743
|
*/
|
|
707
744
|
getAutoScroll() {
|
|
708
|
-
return this.#
|
|
745
|
+
return this.#m;
|
|
709
746
|
}
|
|
710
747
|
isSeriesVisible(t) {
|
|
711
748
|
return this.#t.find((i) => i.id === t)?.visible ?? !0;
|
|
@@ -723,15 +760,15 @@ class ci extends ft {
|
|
|
723
760
|
setLayerVisible(t, i, e) {
|
|
724
761
|
const s = this.#t.find((a) => a.id === t);
|
|
725
762
|
if (!s || s.renderer.getLayerCount() <= 1 || s.renderer.isLayerVisible(i) === e) return;
|
|
726
|
-
s.renderer.setLayerVisible(i, e), this.#
|
|
727
|
-
const r = this.#
|
|
763
|
+
s.renderer.setLayerVisible(i, e), this.#f();
|
|
764
|
+
const r = this.#d.toggleMs;
|
|
728
765
|
if (s.renderer.setLayerAlpha?.(i, e ? 1 : 0, r), this.#i.markDirty(), this.#L > 0) {
|
|
729
|
-
this.#
|
|
766
|
+
this.#V = !0;
|
|
730
767
|
return;
|
|
731
768
|
}
|
|
732
769
|
this.#g = !0;
|
|
733
770
|
const n = performance.now();
|
|
734
|
-
this.#s.onSeriesVisibilityChanged(n), this.#
|
|
771
|
+
this.#s.onSeriesVisibilityChanged(n), this.#b(n);
|
|
735
772
|
}
|
|
736
773
|
isLayerVisible(t, i) {
|
|
737
774
|
return this.#t.find((e) => e.id === t)?.renderer.isLayerVisible(i) ?? !0;
|
|
@@ -741,10 +778,10 @@ class ci extends ft {
|
|
|
741
778
|
const { first: t, last: i } = this.getDataBounds();
|
|
742
779
|
if (t === void 0 || i === void 0) return;
|
|
743
780
|
const e = this.#a.size.media.width - this.yAxisWidth;
|
|
744
|
-
this.#
|
|
781
|
+
this.#O(t, i, e), this.#G({ xEase: !0 });
|
|
745
782
|
}
|
|
746
783
|
getVisibleRange() {
|
|
747
|
-
return this.#
|
|
784
|
+
return this.#h.from === 0 && this.#h.to === 0 ? this.#s.getAnimationState().xRange : this.#h;
|
|
748
785
|
}
|
|
749
786
|
/**
|
|
750
787
|
* Return the full span of registered data, or `null` before any data has
|
|
@@ -752,7 +789,7 @@ class ci extends ft {
|
|
|
752
789
|
* tracking and is cheaper than recomputing from series stores.
|
|
753
790
|
*/
|
|
754
791
|
getDataRange() {
|
|
755
|
-
return this.#
|
|
792
|
+
return this.#T === null || this.#e === null ? null : { from: this.#T, to: this.#e };
|
|
756
793
|
}
|
|
757
794
|
/**
|
|
758
795
|
* Imperatively set the visible time range. Three forms:
|
|
@@ -783,27 +820,27 @@ class ci extends ft {
|
|
|
783
820
|
if (!Number.isInteger(t) || t < 2) return;
|
|
784
821
|
const { first: n, last: a } = this.getDataBounds();
|
|
785
822
|
if (n === void 0 || a === void 0) return;
|
|
786
|
-
const
|
|
787
|
-
this.#
|
|
823
|
+
const o = Math.max(n, a - (t - 1) * this.#r), l = this.#a.size.media.width - this.yAxisWidth;
|
|
824
|
+
this.#O(o, a, l), this.#G();
|
|
788
825
|
return;
|
|
789
826
|
}
|
|
790
827
|
if ("bars" in t) {
|
|
791
828
|
if (!Number.isInteger(t.bars) || t.bars < 2) return;
|
|
792
829
|
const n = x(t.from), a = n + t.bars * this.#r;
|
|
793
|
-
this.#
|
|
830
|
+
this.#m = this.#e !== null && this.#e >= n && this.#e <= a, this.#C({ from: n, to: a }, { emitChange: !0 }), this.#M = !0, this.#G();
|
|
794
831
|
return;
|
|
795
832
|
}
|
|
796
833
|
const e = x(t.from), s = x(t.to);
|
|
797
|
-
this.#
|
|
834
|
+
this.#m = this.#e !== null && this.#e >= e && this.#e <= s, this.#M = !1;
|
|
798
835
|
const r = this.#C({ from: e, to: s }, { emitChange: !0 });
|
|
799
836
|
if (i?.gesture) {
|
|
800
|
-
r && this.#
|
|
837
|
+
r && this.#Qt();
|
|
801
838
|
return;
|
|
802
839
|
}
|
|
803
|
-
this.#
|
|
840
|
+
this.#G();
|
|
804
841
|
}
|
|
805
842
|
getYRange() {
|
|
806
|
-
return this.#
|
|
843
|
+
return this.#B;
|
|
807
844
|
}
|
|
808
845
|
getCrosshairPosition() {
|
|
809
846
|
return this.#c;
|
|
@@ -844,36 +881,28 @@ class ci extends ft {
|
|
|
844
881
|
setCrosshair(t) {
|
|
845
882
|
if (t === null) {
|
|
846
883
|
if (this.#c === null) return;
|
|
847
|
-
this.#c = null, this.#
|
|
884
|
+
this.#c = null, this.#o.markDirty(), this.emit("crosshairMove", null);
|
|
848
885
|
return;
|
|
849
886
|
}
|
|
850
887
|
const { time: i } = t;
|
|
851
888
|
if (!Number.isFinite(i)) return;
|
|
852
889
|
const e = this.#c;
|
|
853
890
|
let s;
|
|
854
|
-
if (t.y !== void 0)
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
s = e.y;
|
|
858
|
-
else {
|
|
859
|
-
const a = this.#z;
|
|
860
|
-
s = (a.min + a.max) / 2;
|
|
861
|
-
}
|
|
862
|
-
if (!Number.isFinite(s) || e !== null && e.time === i && e.y === s) return;
|
|
863
|
-
const r = this.timeScale.timeToX(i), n = this.yScale.valueToY(s);
|
|
864
|
-
this.#$ = "time", this.#c = { mediaX: r, mediaY: n, time: i, y: s }, this.#h.markDirty(), this.emit("crosshairMove", this.#c);
|
|
891
|
+
if (t.y !== void 0 ? s = t.y : e !== null ? s = e.y : s = this.yScale.yToValue(this.yScale.getMediaHeight() / 2), !Number.isFinite(s) || e !== null && e.time === i && e.y === s) return;
|
|
892
|
+
const r = this.plotLeft + this.timeScale.timeToX(i), n = this.yScale.valueToY(s);
|
|
893
|
+
this.#K = "time", this.#c = { mediaX: r, mediaY: n, time: i, y: s }, this.#o.markDirty(), this.emit("crosshairMove", this.#c);
|
|
865
894
|
}
|
|
866
895
|
/** Get the last visible value and whether the absolute last point is on screen. */
|
|
867
896
|
getLastValue(t) {
|
|
868
|
-
return
|
|
897
|
+
return nt(t, this.#t, this.#s.getAnimationState().xRange);
|
|
869
898
|
}
|
|
870
899
|
/** Get the second-to-last value, useful for computing change. */
|
|
871
900
|
getPreviousClose(t) {
|
|
872
|
-
return
|
|
901
|
+
return at(t, this.#t);
|
|
873
902
|
}
|
|
874
903
|
getLastData(t) {
|
|
875
904
|
const i = this.#t.find((e) => e.id === t);
|
|
876
|
-
return !i || !
|
|
905
|
+
return !i || !b(i.renderer) ? null : i.renderer.getLastDataPoint();
|
|
877
906
|
}
|
|
878
907
|
/** Find the data point closest to the given timestamp within one data interval. */
|
|
879
908
|
getDataAtTime(t, i) {
|
|
@@ -891,7 +920,7 @@ class ci extends ft {
|
|
|
891
920
|
return e?.visible ? e.renderer.getLayerSnapshots?.(i, this.#r) ?? null : null;
|
|
892
921
|
}
|
|
893
922
|
getSeriesIds() {
|
|
894
|
-
return this.#
|
|
923
|
+
return this.#P || (this.#P = this.#t.map((t) => t.id)), this.#P.slice();
|
|
895
924
|
}
|
|
896
925
|
/**
|
|
897
926
|
* True if any visible series has a time axis (line / bar / candlestick).
|
|
@@ -899,12 +928,30 @@ class ci extends ft {
|
|
|
899
928
|
* has no meaningful x/y coordinate system, so the dashed hairlines would
|
|
900
929
|
* just be visual noise.
|
|
901
930
|
*/
|
|
902
|
-
#
|
|
931
|
+
#Mt() {
|
|
903
932
|
for (const t of this.#t)
|
|
904
|
-
if (t.visible &&
|
|
933
|
+
if (t.visible && b(t.renderer))
|
|
905
934
|
return !0;
|
|
906
935
|
return !1;
|
|
907
936
|
}
|
|
937
|
+
/**
|
|
938
|
+
* True when series exist and none has a time axis (pie, heatmap): those
|
|
939
|
+
* renderers lay out against the whole canvas, so there are no axis gutters
|
|
940
|
+
* to reserve and nothing slides under an axis column. Reads registration,
|
|
941
|
+
* not visibility — a legend toggle must not shift the layout.
|
|
942
|
+
*/
|
|
943
|
+
#X() {
|
|
944
|
+
return this.#t.length === 0 ? !1 : this.#t.every((t) => !b(t.renderer));
|
|
945
|
+
}
|
|
946
|
+
#Z() {
|
|
947
|
+
return { yAxisWidth: this.yAxisWidth, xAxisHeight: this.xAxisHeight, yAxisPosition: this.yAxisPosition };
|
|
948
|
+
}
|
|
949
|
+
/** Resync the scales after the axis gutters moved, and let DOM axis / label
|
|
950
|
+
* components re-read the layout, as they do after a resize. */
|
|
951
|
+
#et(t) {
|
|
952
|
+
const i = this.#Z();
|
|
953
|
+
(i.yAxisWidth !== t.yAxisWidth || i.xAxisHeight !== t.xAxisHeight || i.yAxisPosition !== t.yAxisPosition) && (this.syncScales(), this.emit("viewportChange"), this.#i.markDirty());
|
|
954
|
+
}
|
|
908
955
|
/**
|
|
909
956
|
* Whether pan/zoom gestures currently apply — true when any visible series
|
|
910
957
|
* has a time axis. Part of the {@link PanZoomTarget} surface: on a
|
|
@@ -912,7 +959,7 @@ class ci extends ft {
|
|
|
912
959
|
* wheel and touch events, so the page scrolls normally under the cursor.
|
|
913
960
|
*/
|
|
914
961
|
canPanZoom() {
|
|
915
|
-
return this.#
|
|
962
|
+
return this.#Mt();
|
|
916
963
|
}
|
|
917
964
|
/**
|
|
918
965
|
* Push the current pan/zoom capability into the interaction layer (canvas
|
|
@@ -921,8 +968,8 @@ class ci extends ft {
|
|
|
921
968
|
* every mutation that can flip {@link canPanZoom}: series add/remove and
|
|
922
969
|
* series visibility.
|
|
923
970
|
*/
|
|
924
|
-
#
|
|
925
|
-
this.#
|
|
971
|
+
#st() {
|
|
972
|
+
this.#D?.syncPanZoomMode();
|
|
926
973
|
}
|
|
927
974
|
/**
|
|
928
975
|
* Filter `getSeriesIds()` by renderer type. A concrete kind (`'pie'`,
|
|
@@ -938,19 +985,19 @@ class ci extends ft {
|
|
|
938
985
|
getSeriesIdsByType(t, i) {
|
|
939
986
|
const e = i?.visibleOnly === !0, s = i?.singleLayerOnly === !0, r = [];
|
|
940
987
|
for (const n of this.#t) {
|
|
941
|
-
if (!(t === "time" ?
|
|
988
|
+
if (!(t === "time" ? b(n.renderer) : n.renderer.kind === t)) continue;
|
|
942
989
|
const a = n.renderer.getLayerCount();
|
|
943
990
|
if (!(s && a > 1)) {
|
|
944
991
|
if (e) {
|
|
945
992
|
if (!n.visible) continue;
|
|
946
993
|
if (a > 1) {
|
|
947
|
-
let
|
|
994
|
+
let o = !1;
|
|
948
995
|
for (let l = 0; l < a; l++)
|
|
949
996
|
if (n.renderer.isLayerVisible(l)) {
|
|
950
|
-
|
|
997
|
+
o = !0;
|
|
951
998
|
break;
|
|
952
999
|
}
|
|
953
|
-
if (!
|
|
1000
|
+
if (!o) continue;
|
|
954
1001
|
}
|
|
955
1002
|
}
|
|
956
1003
|
r.push(n.id);
|
|
@@ -965,7 +1012,7 @@ class ci extends ft {
|
|
|
965
1012
|
* Returns `null` for unknown ids or empty series.
|
|
966
1013
|
*/
|
|
967
1014
|
getStackedLastValue(t) {
|
|
968
|
-
return
|
|
1015
|
+
return ot(t, this.#t, this.#s.getAnimationState().xRange);
|
|
969
1016
|
}
|
|
970
1017
|
/**
|
|
971
1018
|
* Stacked value at an arbitrary `time` — the cumulative stack top at that
|
|
@@ -1022,7 +1069,7 @@ class ci extends ft {
|
|
|
1022
1069
|
*/
|
|
1023
1070
|
setSeriesLabels(t, i) {
|
|
1024
1071
|
const e = this.#t.find((s) => s.id === t);
|
|
1025
|
-
!e ||
|
|
1072
|
+
!e || Z(e.labels, i) || (e.labels = i, this.#f());
|
|
1026
1073
|
}
|
|
1027
1074
|
/**
|
|
1028
1075
|
* Set the per-layer color overrides carried in `data` (sparse — `undefined`
|
|
@@ -1033,7 +1080,7 @@ class ci extends ft {
|
|
|
1033
1080
|
*/
|
|
1034
1081
|
setLayerColors(t, i) {
|
|
1035
1082
|
const e = this.#t.find((s) => s.id === t);
|
|
1036
|
-
!e ||
|
|
1083
|
+
!e || Xt(e.colorOverrides, i) || (e.colorOverrides = i, e.renderer.setColorOverrides?.(i), this.#i.markDirty(), this.#f());
|
|
1037
1084
|
}
|
|
1038
1085
|
/** Get per-layer colors for a series. Returns null for single-layer non-bar/line series. */
|
|
1039
1086
|
getSeriesLayers(t) {
|
|
@@ -1058,31 +1105,34 @@ class ci extends ft {
|
|
|
1058
1105
|
this.#n = t;
|
|
1059
1106
|
for (const e of this.#t)
|
|
1060
1107
|
e.renderer.applyTheme(t, i);
|
|
1061
|
-
this.#i.markDirty(), this.#
|
|
1108
|
+
this.#i.markDirty(), this.#f();
|
|
1062
1109
|
}
|
|
1063
1110
|
getTheme() {
|
|
1064
1111
|
return this.#n;
|
|
1065
1112
|
}
|
|
1066
1113
|
/** BCP 47 locale applied to the built-in time-axis / tooltip / crosshair formatting. */
|
|
1067
1114
|
setLocale(t) {
|
|
1068
|
-
this.timeScale.setLocale(t), this.#
|
|
1115
|
+
this.timeScale.setLocale(t), this.#f();
|
|
1069
1116
|
}
|
|
1070
1117
|
getLocale() {
|
|
1071
1118
|
return this.timeScale.getLocale();
|
|
1072
1119
|
}
|
|
1073
1120
|
/** IANA timezone applied to the same built-in date-time formatting. */
|
|
1074
1121
|
setTimeZone(t) {
|
|
1075
|
-
this.timeScale.setTimeZone(t), this.#
|
|
1122
|
+
this.timeScale.setTimeZone(t), this.#f();
|
|
1076
1123
|
}
|
|
1077
1124
|
getTimeZone() {
|
|
1078
1125
|
return this.timeScale.getTimeZone();
|
|
1079
1126
|
}
|
|
1080
1127
|
/** Update axis configuration and re-render. */
|
|
1081
1128
|
setAxis(t) {
|
|
1082
|
-
const i =
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1129
|
+
const i = t.y?.scale ?? null, e = (a) => Mt(i, a);
|
|
1130
|
+
for (const a of this.#t)
|
|
1131
|
+
b(a.renderer) && M({ seriesId: a.id, stacking: a.renderer.stacking, isPlottable: e });
|
|
1132
|
+
const s = this.#Z(), r = i !== this.yScale.getTransform();
|
|
1133
|
+
this.#w = t, this.#p = { min: t.y?.min, max: t.y?.max }, E({ y: t.y, isPlottable: e });
|
|
1134
|
+
const n = performance.now();
|
|
1135
|
+
r && (this.yScale.setTransform(i), this.yScale.tickTracker.reset(), this.#ht() === null && this.#s.snap({ y: { min: 0, max: 0 } }, n)), this.#g = !0, this.#s.onAxisReconfig(n), this.#b(n), this.#et(s), this.#i.markDirty();
|
|
1086
1136
|
}
|
|
1087
1137
|
/**
|
|
1088
1138
|
* Apply label-density knobs driven by `<YAxis labelCount=… minLabelSpacing=…>`
|
|
@@ -1106,8 +1156,9 @@ class ci extends ft {
|
|
|
1106
1156
|
getLayout() {
|
|
1107
1157
|
const t = this.#a.size.media, i = this.yAxisWidth, e = this.xAxisHeight;
|
|
1108
1158
|
return {
|
|
1109
|
-
chartArea: { x:
|
|
1159
|
+
chartArea: { x: this.plotLeft, y: 0, width: t.width - i, height: t.height - e },
|
|
1110
1160
|
yAxisWidth: i,
|
|
1161
|
+
yAxisPosition: this.yAxisPosition,
|
|
1111
1162
|
xAxisHeight: e
|
|
1112
1163
|
};
|
|
1113
1164
|
}
|
|
@@ -1124,19 +1175,19 @@ class ci extends ft {
|
|
|
1124
1175
|
*/
|
|
1125
1176
|
setPadding(t) {
|
|
1126
1177
|
const i = this.#l;
|
|
1127
|
-
this.#l =
|
|
1128
|
-
const e = this.#l, s = !
|
|
1178
|
+
this.#l = F(t);
|
|
1179
|
+
const e = this.#l, s = !N(i.left, e.left) || !N(i.right, e.right), r = i.top !== e.top || i.bottom !== e.bottom;
|
|
1129
1180
|
if (s) {
|
|
1130
1181
|
const { first: n, last: a } = this.getDataBounds();
|
|
1131
1182
|
if (n !== void 0 && a !== void 0) {
|
|
1132
|
-
const
|
|
1133
|
-
this.#
|
|
1183
|
+
const o = this.#a.size.media.width - this.yAxisWidth;
|
|
1184
|
+
this.#O(n, a, o);
|
|
1134
1185
|
}
|
|
1135
1186
|
}
|
|
1136
1187
|
if (s || r) {
|
|
1137
1188
|
this.#g = !0;
|
|
1138
1189
|
const n = performance.now();
|
|
1139
|
-
this.#s.onAxisReconfig(n), this.#
|
|
1190
|
+
this.#s.onAxisReconfig(n), this.#b(n);
|
|
1140
1191
|
}
|
|
1141
1192
|
this.syncScales(), this.#i.markDirty(), r && this.emit("viewportChange");
|
|
1142
1193
|
}
|
|
@@ -1152,8 +1203,14 @@ class ci extends ft {
|
|
|
1152
1203
|
* the next render frame.
|
|
1153
1204
|
*/
|
|
1154
1205
|
setFade(t) {
|
|
1155
|
-
const i =
|
|
1156
|
-
i.top === e.top && i.right === e.right && i.left === e.left || (this.#
|
|
1206
|
+
const i = O(t), e = this.#A;
|
|
1207
|
+
i.top === e.top && i.right === e.right && i.left === e.left || (this.#A = i, this.#i.markDirty());
|
|
1208
|
+
}
|
|
1209
|
+
/** Report the floating header's height (see {@link headerHeight}). The
|
|
1210
|
+
* framework wrappers keep it in sync with the measured Title / InfoBar. */
|
|
1211
|
+
setHeaderHeight(t) {
|
|
1212
|
+
const i = Number.isFinite(t) && t > 0 ? t : 0;
|
|
1213
|
+
i !== this.#U && (this.#U = i, this.#f());
|
|
1157
1214
|
}
|
|
1158
1215
|
/**
|
|
1159
1216
|
* Set the visual state for one side of the chart. Typically called in
|
|
@@ -1165,23 +1222,23 @@ class ci extends ft {
|
|
|
1165
1222
|
* symmetry and currently renders identically to `idle`.
|
|
1166
1223
|
*/
|
|
1167
1224
|
setEdgeState(t, i) {
|
|
1168
|
-
if (this.#
|
|
1169
|
-
const e = this.#
|
|
1170
|
-
this.#
|
|
1225
|
+
if (this.#S[t] === i) return;
|
|
1226
|
+
const e = this.#S[t] === "loading", s = this.#z[t];
|
|
1227
|
+
this.#z[t] = !1, this.#N[t] = e && !s && (i === "idle" || i === "has-more") ? performance.now() : null, this.#S[t] = i, this.#o.markDirty();
|
|
1171
1228
|
}
|
|
1172
1229
|
/** Read the current host-declared state for a given edge. */
|
|
1173
1230
|
getEdgeState(t) {
|
|
1174
|
-
return this.#
|
|
1231
|
+
return this.#S[t];
|
|
1175
1232
|
}
|
|
1176
1233
|
/**
|
|
1177
1234
|
* Alpha for a side's `loading` exit fade, or `null` when none is in
|
|
1178
1235
|
* flight. Self-cleaning: an elapsed fade clears its start stamp.
|
|
1179
1236
|
*/
|
|
1180
|
-
#
|
|
1237
|
+
#Dt(t) {
|
|
1181
1238
|
const i = this.#N[t];
|
|
1182
1239
|
if (i === null) return null;
|
|
1183
|
-
const e = (performance.now() - i) /
|
|
1184
|
-
return e >= 1 ? (this.#N[t] = null, null) : 1 -
|
|
1240
|
+
const e = (performance.now() - i) / zt;
|
|
1241
|
+
return e >= 1 ? (this.#N[t] = null, null) : 1 - W(Math.max(0, e));
|
|
1185
1242
|
}
|
|
1186
1243
|
/**
|
|
1187
1244
|
* Swap the painter used for a side's `loading` spinner — the same
|
|
@@ -1190,7 +1247,7 @@ class ci extends ft {
|
|
|
1190
1247
|
* immediately if that side is currently in the `loading` state.
|
|
1191
1248
|
*/
|
|
1192
1249
|
setEdgeIndicator(t, i) {
|
|
1193
|
-
this.#
|
|
1250
|
+
this.#vt[t] = i, this.#S[t] === "loading" && this.#o.markDirty();
|
|
1194
1251
|
}
|
|
1195
1252
|
/**
|
|
1196
1253
|
* Resolve the boundary point's Y position and series kind on the primary
|
|
@@ -1200,14 +1257,14 @@ class ci extends ft {
|
|
|
1200
1257
|
* `open` (the value immediately preceding the boundary); `right` from the
|
|
1201
1258
|
* last visible candle's `close`.
|
|
1202
1259
|
*/
|
|
1203
|
-
#
|
|
1260
|
+
#Ut(t, i) {
|
|
1204
1261
|
const e = this.getSeriesIdsByType("time", { visibleOnly: !0, singleLayerOnly: !0 })[0] ?? this.getSeriesIdsByType("time", { visibleOnly: !0 })[0];
|
|
1205
1262
|
if (!e) return null;
|
|
1206
1263
|
const s = this.getDataAtTime(e, i);
|
|
1207
1264
|
if (!s) return null;
|
|
1208
1265
|
const r = this.#t.find((n) => n.id === e);
|
|
1209
1266
|
return r ? {
|
|
1210
|
-
valueY: this.yScale.valueToY(
|
|
1267
|
+
valueY: this.yScale.valueToY(it(t, s)),
|
|
1211
1268
|
seriesKind: r.renderer.kind
|
|
1212
1269
|
} : null;
|
|
1213
1270
|
}
|
|
@@ -1217,7 +1274,7 @@ class ci extends ft {
|
|
|
1217
1274
|
* placeholder shapes to match the real bars that will render once data
|
|
1218
1275
|
* lands. `null` when the viewport has no meaningful span yet.
|
|
1219
1276
|
*/
|
|
1220
|
-
#
|
|
1277
|
+
#Kt() {
|
|
1221
1278
|
const t = this.timeScale.barWidthMedia(this.#r);
|
|
1222
1279
|
return Number.isFinite(t) && t > 0 ? t : null;
|
|
1223
1280
|
}
|
|
@@ -1236,7 +1293,7 @@ class ci extends ft {
|
|
|
1236
1293
|
* probed, not kind-gated: a kind check here silently kills hover for the
|
|
1237
1294
|
* next spatial renderer.
|
|
1238
1295
|
*/
|
|
1239
|
-
#
|
|
1296
|
+
#rt(t) {
|
|
1240
1297
|
const i = this.#a.size, e = this.#l, s = {
|
|
1241
1298
|
top: e.top * i.verticalPixelRatio,
|
|
1242
1299
|
bottom: e.bottom * i.verticalPixelRatio
|
|
@@ -1244,12 +1301,12 @@ class ci extends ft {
|
|
|
1244
1301
|
let r = !1, n = null;
|
|
1245
1302
|
for (const a of this.#t) {
|
|
1246
1303
|
if (!a.renderer.hitTest || !a.renderer.setHoverIndex) continue;
|
|
1247
|
-
let
|
|
1304
|
+
let o = -1;
|
|
1248
1305
|
if (t) {
|
|
1249
|
-
const l = t.mediaX * i.horizontalPixelRatio,
|
|
1250
|
-
|
|
1306
|
+
const l = (t.mediaX - this.plotLeft) * i.horizontalPixelRatio, h = t.mediaY * i.verticalPixelRatio;
|
|
1307
|
+
o = a.renderer.hitTest(l, h, i.bitmap.width, i.bitmap.height, s);
|
|
1251
1308
|
}
|
|
1252
|
-
a.renderer.setHoverIndex(
|
|
1309
|
+
a.renderer.setHoverIndex(o) && (r = !0), o >= 0 && !n && (n = { seriesId: a.id, index: o, info: a.renderer.getHoverInfo?.(this.#n) ?? null });
|
|
1253
1310
|
}
|
|
1254
1311
|
r && (this.#i.markDirty(), this.emit("seriesHover", n));
|
|
1255
1312
|
}
|
|
@@ -1260,15 +1317,15 @@ class ci extends ft {
|
|
|
1260
1317
|
* Returns the first spatial series (pie, heatmap, or a custom spatial kind)
|
|
1261
1318
|
* whose `hitTest` claims the position, or `null` if none does.
|
|
1262
1319
|
*/
|
|
1263
|
-
#
|
|
1320
|
+
#Pt(t) {
|
|
1264
1321
|
const i = this.#a.size, e = this.#l, s = {
|
|
1265
1322
|
top: e.top * i.verticalPixelRatio,
|
|
1266
1323
|
bottom: e.bottom * i.verticalPixelRatio
|
|
1267
|
-
}, r = t.mediaX * i.horizontalPixelRatio, n = t.mediaY * i.verticalPixelRatio;
|
|
1324
|
+
}, r = (t.mediaX - this.plotLeft) * i.horizontalPixelRatio, n = t.mediaY * i.verticalPixelRatio;
|
|
1268
1325
|
for (const a of this.#t) {
|
|
1269
1326
|
if (!a.renderer.hitTest) continue;
|
|
1270
|
-
const
|
|
1271
|
-
if (
|
|
1327
|
+
const o = a.renderer.hitTest(r, n, i.bitmap.width, i.bitmap.height, s);
|
|
1328
|
+
if (o >= 0) return { seriesId: a.id, index: o };
|
|
1272
1329
|
}
|
|
1273
1330
|
return null;
|
|
1274
1331
|
}
|
|
@@ -1292,40 +1349,40 @@ class ci extends ft {
|
|
|
1292
1349
|
* viewport didn't move, the re-derived values are bit-identical and the
|
|
1293
1350
|
* method exits without emitting.
|
|
1294
1351
|
*/
|
|
1295
|
-
#
|
|
1352
|
+
#jt() {
|
|
1296
1353
|
const t = this.#c;
|
|
1297
1354
|
if (t === null) return;
|
|
1298
1355
|
const { from: i, to: e } = this.timeScale.getRange(), s = this.yScale.getRange(), r = Math.abs(e - i) * 1e-9, n = Math.abs(s.max - s.min) * 1e-9, a = 1e-6;
|
|
1299
|
-
let
|
|
1300
|
-
if (this
|
|
1301
|
-
const l = this.timeScale.xToTime(t.mediaX),
|
|
1302
|
-
if (!Number.isFinite(l) || !Number.isFinite(
|
|
1303
|
-
|
|
1356
|
+
let o;
|
|
1357
|
+
if (this.#K === "pointer") {
|
|
1358
|
+
const l = this.timeScale.xToTime(t.mediaX - this.plotLeft), h = this.yScale.yToValue(t.mediaY);
|
|
1359
|
+
if (!Number.isFinite(l) || !Number.isFinite(h) || Math.abs(l - t.time) <= r && Math.abs(h - t.y) <= n) return;
|
|
1360
|
+
o = { mediaX: t.mediaX, mediaY: t.mediaY, time: l, y: h };
|
|
1304
1361
|
} else {
|
|
1305
1362
|
if (t.time < i || t.time > e) {
|
|
1306
|
-
this.#c = null, this.#
|
|
1363
|
+
this.#c = null, this.#o.markDirty(), this.emit("crosshairMove", null), this.#rt(null);
|
|
1307
1364
|
return;
|
|
1308
1365
|
}
|
|
1309
|
-
const l = this.timeScale.timeToX(t.time),
|
|
1310
|
-
if (!Number.isFinite(l) || !Number.isFinite(
|
|
1311
|
-
|
|
1366
|
+
const l = this.plotLeft + this.timeScale.timeToX(t.time), h = this.yScale.valueToY(t.y);
|
|
1367
|
+
if (!Number.isFinite(l) || !Number.isFinite(h) || Math.abs(l - t.mediaX) <= a && Math.abs(h - t.mediaY) <= a) return;
|
|
1368
|
+
o = { mediaX: l, mediaY: h, time: t.time, y: t.y };
|
|
1312
1369
|
}
|
|
1313
|
-
this.#c =
|
|
1370
|
+
this.#c = o, this.#o.markDirty(), this.emit("crosshairMove", o), this.#rt(o);
|
|
1314
1371
|
}
|
|
1315
1372
|
/** Tear down the chart: cancel animations, remove listeners, and detach the canvas. */
|
|
1316
1373
|
destroy() {
|
|
1317
1374
|
for (const t of this.#t) t.renderer.dispose();
|
|
1318
|
-
this.#t = [], this.#u = [], this.#x = [], this.#y = [], this.#
|
|
1375
|
+
this.#t = [], this.#u = [], this.#x = [], this.#y = [], this.#P = null, this.#i.destroy(), this.#o.destroy(), this.#D?.destroy(), this.#a.destroy(), this.#tt?.destroy(), this.#tt = null, this.#Q && this.#k?.destroy(), this.#k = null, this.#Q = !1, this.removeAllListeners();
|
|
1319
1376
|
}
|
|
1320
1377
|
/** The attached performance monitor, or `null` when instrumentation is disabled. */
|
|
1321
1378
|
getPerfMonitor() {
|
|
1322
|
-
return this.#
|
|
1379
|
+
return this.#k;
|
|
1323
1380
|
}
|
|
1324
1381
|
/** Compute the earliest and latest timestamps across all series. */
|
|
1325
1382
|
getDataBounds() {
|
|
1326
1383
|
let t, i;
|
|
1327
1384
|
for (const e of this.#t) {
|
|
1328
|
-
if (!e.visible || !
|
|
1385
|
+
if (!e.visible || !b(e.renderer)) continue;
|
|
1329
1386
|
const s = e.renderer.getTimeBounds();
|
|
1330
1387
|
s !== null && ((t === void 0 || s.first < t) && (t = s.first), (i === void 0 || s.last > i) && (i = s.last));
|
|
1331
1388
|
}
|
|
@@ -1334,28 +1391,28 @@ class ci extends ft {
|
|
|
1334
1391
|
/** Set by `setSeriesData`/bulk-replace paths so the next `onDataChanged`
|
|
1335
1392
|
* snaps the Y range (instead of easing) and `yScale.getRange()` reflects
|
|
1336
1393
|
* the new domain synchronously. Cleared by `onDataChanged` after each run. */
|
|
1337
|
-
#
|
|
1394
|
+
#nt = !1;
|
|
1338
1395
|
onDataChanged() {
|
|
1339
1396
|
if (this.#L > 0) {
|
|
1340
|
-
this.#
|
|
1397
|
+
this.#q = !0;
|
|
1341
1398
|
return;
|
|
1342
1399
|
}
|
|
1343
1400
|
this.updateDataInterval();
|
|
1344
1401
|
const { first: t, last: i } = this.getDataBounds();
|
|
1345
|
-
t !== void 0 && (this.#
|
|
1346
|
-
const e = this.#
|
|
1347
|
-
this.#
|
|
1402
|
+
t !== void 0 && (this.#T = t), i !== void 0 && (this.#E = this.#e, this.#e = i);
|
|
1403
|
+
const e = this.#nt;
|
|
1404
|
+
this.#nt = !1;
|
|
1348
1405
|
const s = !this.#g && t !== void 0;
|
|
1349
|
-
e && (this.yScale.tickTracker.reset(), this.timeScale.tickTracker.reset()), this.#
|
|
1406
|
+
e && (this.yScale.tickTracker.reset(), this.timeScale.tickTracker.reset()), this.#xt = e;
|
|
1350
1407
|
const r = performance.now();
|
|
1351
|
-
e || s ? (this.#R.seed(r), this.#s.onDataReplaced(r)) : this.#s.onPointAppended(r), this.#s.lastXTarget !== null && (this.#
|
|
1408
|
+
e || s ? (this.#R.seed(r), this.#s.onDataReplaced(r)) : this.#s.onPointAppended(r), this.#s.lastXTarget !== null && (this.#W = !0), t !== void 0 && (this.#g = !0), this.#b(r), this.syncScales(), s ? this.#dt() : this.#i.markDirty(), this.emit("dataUpdate"), this.#f();
|
|
1352
1409
|
}
|
|
1353
1410
|
updateDataInterval() {
|
|
1354
1411
|
for (const t of this.#t) {
|
|
1355
|
-
if (!
|
|
1412
|
+
if (!b(t.renderer)) continue;
|
|
1356
1413
|
const i = t.renderer.sampleTimes(20);
|
|
1357
1414
|
if (i.length >= 2) {
|
|
1358
|
-
this.#r =
|
|
1415
|
+
this.#r = Ht(i);
|
|
1359
1416
|
break;
|
|
1360
1417
|
}
|
|
1361
1418
|
}
|
|
@@ -1371,36 +1428,52 @@ class ci extends ft {
|
|
|
1371
1428
|
* range — without this guard the engine would ease from `{0,0}` into
|
|
1372
1429
|
* the new target and the very first frame would render with a
|
|
1373
1430
|
* zero-width X domain. */
|
|
1374
|
-
#
|
|
1431
|
+
#W = !1;
|
|
1375
1432
|
/**
|
|
1376
1433
|
* One-shot `setVisibleRange` argument applied on the first data arrival.
|
|
1377
1434
|
* Captured at chart construction from {@link ChartOptions.viewport.initialRange};
|
|
1378
1435
|
* cleared the moment it's consumed so subsequent data swaps don't snap
|
|
1379
1436
|
* back to the initial window.
|
|
1380
1437
|
*/
|
|
1381
|
-
#
|
|
1438
|
+
#_;
|
|
1382
1439
|
/** Last Y bounds the engine resolved into {@link AnimationState.yRange}.
|
|
1383
1440
|
* Used by {@link renderMain} to detect Y movement frame-to-frame so
|
|
1384
1441
|
* `viewportChange` only emits when the Y range actually shifted — DOM
|
|
1385
1442
|
* axis labels subscribe to that event and re-renders on every tick are
|
|
1386
1443
|
* expensive. Initialised to NaN so the first real Y emit always passes
|
|
1387
1444
|
* the change check. */
|
|
1388
|
-
#
|
|
1389
|
-
#
|
|
1390
|
-
/**
|
|
1391
|
-
|
|
1392
|
-
|
|
1445
|
+
#at = Number.NaN;
|
|
1446
|
+
#ot = Number.NaN;
|
|
1447
|
+
/** Dev warning for values the Y scale can't place — fires once per chart. */
|
|
1448
|
+
#Rt = !1;
|
|
1449
|
+
/**
|
|
1450
|
+
* Sample the visible data window and return the resolved Y bounds in scale
|
|
1451
|
+
* space (see {@link resolveYTarget}), or `null` when no series has data
|
|
1452
|
+
* inside the X destination range.
|
|
1393
1453
|
* Sampling runs against `logicalRange` (the X target, not the animating
|
|
1394
1454
|
* current), so Y stays on a stable target while X eases — both dimensions
|
|
1395
1455
|
* converge together. Clears {@link #yInited} on empty so the next emit
|
|
1396
1456
|
* with valid data lands as an instant snap.
|
|
1397
1457
|
*/
|
|
1398
|
-
#
|
|
1399
|
-
const i = t ?? this.#
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1458
|
+
#ht(t) {
|
|
1459
|
+
const i = t ?? this.#h, e = this.#p.min !== void 0 && this.#p.min !== "auto" && typeof this.#p.min != "number" || this.#p.max !== void 0 && this.#p.max !== "auto" && typeof this.#p.max != "number" ? [] : null, s = this.yScale.getTransform(), r = s === null ? void 0 : this.#Y;
|
|
1460
|
+
r && this.#qt(i);
|
|
1461
|
+
const n = pt({ targetVisible: i, series: this.#t, allValues: e, plottable: r });
|
|
1462
|
+
return n === null ? (this.#g = !1, null) : yt({ raw: n, bounds: this.#p, allValues: e ?? [], transform: s });
|
|
1463
|
+
}
|
|
1464
|
+
/** Dev-only sweep behind {@link warnUnplottableValues}; stops once it has warned. */
|
|
1465
|
+
#qt(t) {
|
|
1466
|
+
if (!(this.#Rt || process.env.NODE_ENV === "production"))
|
|
1467
|
+
for (const i of this.#t) {
|
|
1468
|
+
if (!i.visible || !b(i.renderer)) continue;
|
|
1469
|
+
const e = i.renderer.getValueRange(t.from, t.to);
|
|
1470
|
+
if (e === null) continue;
|
|
1471
|
+
const s = (i.renderer.stacking === "normal" ? e.min < 0 && !this.yScale.isPlottable(e.min) : !this.yScale.isPlottable(e.min)) ? e.min : this.yScale.isPlottable(e.max) ? null : e.max;
|
|
1472
|
+
if (s !== null) {
|
|
1473
|
+
this.#Rt = !0, ut({ seriesId: i.id, value: s });
|
|
1474
|
+
return;
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1404
1477
|
}
|
|
1405
1478
|
/**
|
|
1406
1479
|
* Engine pull-on-demand X target. Called by the engine's `computeXTarget`
|
|
@@ -1420,29 +1493,29 @@ class ci extends ft {
|
|
|
1420
1493
|
* - **autoscroll off** → `null` (no X retarget; user has panned away
|
|
1421
1494
|
* from the tail).
|
|
1422
1495
|
*/
|
|
1423
|
-
#
|
|
1424
|
-
if (this.#
|
|
1425
|
-
const { from: t, to: i } = this.#
|
|
1496
|
+
#Jt() {
|
|
1497
|
+
if (this.#T === null || this.#e === null) return null;
|
|
1498
|
+
const { from: t, to: i } = this.#h, e = t === 0 && i === 0, s = this.#a.size.media.width - this.yAxisWidth;
|
|
1426
1499
|
if (e) {
|
|
1427
|
-
if (this.#
|
|
1428
|
-
const { to: n } = this.#
|
|
1429
|
-
this.#e >= n && this.#e - n <= this.#r * 1.5 && (this.#
|
|
1500
|
+
if (this.#O(this.#T, this.#e, s), this.#_ !== void 0 && (this.setVisibleRange(this.#_), this.#_ = void 0, !this.#m && this.#e !== null)) {
|
|
1501
|
+
const { to: n } = this.#h;
|
|
1502
|
+
this.#e >= n && this.#e - n <= this.#r * 1.5 && (this.#m = !0);
|
|
1430
1503
|
}
|
|
1431
|
-
return this.#
|
|
1504
|
+
return this.#h;
|
|
1432
1505
|
}
|
|
1433
|
-
if (!this.#
|
|
1434
|
-
if (this.#
|
|
1435
|
-
return this.#
|
|
1436
|
-
const r =
|
|
1437
|
-
currentLogical: this.#
|
|
1506
|
+
if (!this.#m) return null;
|
|
1507
|
+
if (this.#xt)
|
|
1508
|
+
return this.#O(this.#T, this.#e, s), this.#h;
|
|
1509
|
+
const r = ft({
|
|
1510
|
+
currentLogical: this.#h,
|
|
1438
1511
|
lastTime: this.#e,
|
|
1439
|
-
prevDataEnd: this.#
|
|
1512
|
+
prevDataEnd: this.#E,
|
|
1440
1513
|
dataInterval: this.#r,
|
|
1441
1514
|
paddingRight: this.#l.right,
|
|
1442
1515
|
chartWidth: s,
|
|
1443
|
-
holdUntilFilled: this.#
|
|
1516
|
+
holdUntilFilled: this.#M
|
|
1444
1517
|
});
|
|
1445
|
-
return r.releaseHold && (this.#
|
|
1518
|
+
return r.releaseHold && (this.#M = !1), r.reengageAutoScroll && (this.#m = !0), r.newLogical === null ? null : (this.#R.observe(performance.now()), this.#s.setXSettleMs(this.#R.pickSettleMs(this.#d.axis.x.settleMs)), this.#C(r.newLogical, { emitChange: !1, skipValidation: !0 }), this.#E = this.#e, r.newLogical);
|
|
1446
1519
|
}
|
|
1447
1520
|
/**
|
|
1448
1521
|
* Fit the viewport's logical X range around a data span. Mirrors the
|
|
@@ -1450,16 +1523,16 @@ class ci extends ft {
|
|
|
1450
1523
|
* forces autoscroll on (data tail is in the new window by construction),
|
|
1451
1524
|
* commits the new range via `viewport.setRange` (which emits `change`).
|
|
1452
1525
|
*/
|
|
1453
|
-
#
|
|
1454
|
-
const s = this.#l, r =
|
|
1526
|
+
#O(t, i, e) {
|
|
1527
|
+
const s = this.#l, r = rt({
|
|
1455
1528
|
firstTime: t,
|
|
1456
1529
|
lastTime: i,
|
|
1457
1530
|
dataInterval: this.#r,
|
|
1458
|
-
maxVisibleBars: this.#
|
|
1531
|
+
maxVisibleBars: this.#St,
|
|
1459
1532
|
chartWidth: e,
|
|
1460
1533
|
padding: { left: s.left, right: s.right }
|
|
1461
1534
|
});
|
|
1462
|
-
this.#
|
|
1535
|
+
this.#M = !1, this.#m = !0, this.#C(r, { emitChange: !0 });
|
|
1463
1536
|
}
|
|
1464
1537
|
/**
|
|
1465
1538
|
* Commit a programmatic zoom — `fitContent` or `setVisibleRange`. Reads
|
|
@@ -1475,15 +1548,15 @@ class ci extends ft {
|
|
|
1475
1548
|
* passes `xEase: true` so X eases instead — viewport and axis converge
|
|
1476
1549
|
* on the same frame.
|
|
1477
1550
|
*/
|
|
1478
|
-
#
|
|
1479
|
-
const i = this.#
|
|
1551
|
+
#G(t) {
|
|
1552
|
+
const i = this.#h;
|
|
1480
1553
|
if (i.to <= i.from) return;
|
|
1481
1554
|
const e = performance.now();
|
|
1482
|
-
if (!this.#
|
|
1483
|
-
this.#
|
|
1555
|
+
if (!this.#W || !this.#g) {
|
|
1556
|
+
this.#Ct(e);
|
|
1484
1557
|
return;
|
|
1485
1558
|
}
|
|
1486
|
-
this.#s.onProgrammaticZoom({ xTarget: i, xEase: t?.xEase }, e), this.#
|
|
1559
|
+
this.#s.onProgrammaticZoom({ xTarget: i, xEase: t?.xEase }, e), this.#b(e);
|
|
1487
1560
|
}
|
|
1488
1561
|
/**
|
|
1489
1562
|
* Commit the current logical X to the engine on the *gesture* profile:
|
|
@@ -1497,15 +1570,15 @@ class ci extends ft {
|
|
|
1497
1570
|
* instead of lagging on the long sticky contract. First paint still snaps
|
|
1498
1571
|
* (same as the programmatic path) so synchronous readers see final values.
|
|
1499
1572
|
*/
|
|
1500
|
-
#
|
|
1501
|
-
const t = this.#
|
|
1573
|
+
#Qt() {
|
|
1574
|
+
const t = this.#h;
|
|
1502
1575
|
if (t.to <= t.from) return;
|
|
1503
1576
|
const i = performance.now();
|
|
1504
|
-
if (!this.#
|
|
1505
|
-
this.#
|
|
1577
|
+
if (!this.#W || !this.#g) {
|
|
1578
|
+
this.#Ct(i);
|
|
1506
1579
|
return;
|
|
1507
1580
|
}
|
|
1508
|
-
this.#s.onPanZoom({ xTarget: t, yAuto: !0 }, i), this.#
|
|
1581
|
+
this.#s.onPanZoom({ xTarget: t, yAuto: !0 }, i), this.#b(i);
|
|
1509
1582
|
}
|
|
1510
1583
|
/**
|
|
1511
1584
|
* First-paint snap: land both axes on the current logical X immediately
|
|
@@ -1514,9 +1587,9 @@ class ci extends ft {
|
|
|
1514
1587
|
* commits — both must snap before the chart has painted once so a
|
|
1515
1588
|
* synchronous `getYRange()` / `getVisibleRange()` read sees final values.
|
|
1516
1589
|
*/
|
|
1517
|
-
#
|
|
1518
|
-
const i = this.#
|
|
1519
|
-
this.#
|
|
1590
|
+
#Ct(t) {
|
|
1591
|
+
const i = this.#h, e = this.#ht(i);
|
|
1592
|
+
this.#W = !0, e !== null && (this.#g = !0), this.#s.snap({ x: i, y: e ?? void 0 }, t), this.#b(t);
|
|
1520
1593
|
}
|
|
1521
1594
|
/**
|
|
1522
1595
|
* Validate and commit a new logical X range. Mirrors the old
|
|
@@ -1538,28 +1611,37 @@ class ci extends ft {
|
|
|
1538
1611
|
*/
|
|
1539
1612
|
#C(t, i) {
|
|
1540
1613
|
const { from: e, to: s } = t;
|
|
1541
|
-
return !Number.isFinite(e) || !Number.isFinite(s) || s <= e || !i.skipValidation && (s - e) / this.#r < 2 || this.#
|
|
1614
|
+
return !Number.isFinite(e) || !Number.isFinite(s) || s <= e || !i.skipValidation && (s - e) / this.#r < 2 || this.#h.from === e && this.#h.to === s ? !1 : (this.#h = { from: e, to: s }, i.emitChange && (this.syncScales(), this.#i?.markDirty(), this.emit("viewportChange")), !0);
|
|
1542
1615
|
}
|
|
1543
1616
|
/**
|
|
1544
|
-
* Apply pixel-padding to the engine's
|
|
1545
|
-
*
|
|
1617
|
+
* Apply pixel-padding to the engine's Y (scale space) and return it as
|
|
1618
|
+
* values for `#yRange`. Mirrors the old `Viewport.setYRange` contract:
|
|
1546
1619
|
* symmetric pad top / bottom from `#padding`, suppressed on the side
|
|
1547
|
-
* that has an explicit (fixed) axis bound.
|
|
1620
|
+
* that has an explicit (fixed) axis bound. Padding in scale space keeps
|
|
1621
|
+
* the pixel gutters even on a log axis.
|
|
1548
1622
|
*/
|
|
1549
|
-
#
|
|
1550
|
-
const { min: i, max: e, chartHeight: s } = t, { min: r, max: n } = this.#
|
|
1551
|
-
return {
|
|
1623
|
+
#It(t) {
|
|
1624
|
+
const { min: i, max: e, chartHeight: s } = t, { min: r, max: n } = this.#ti(), a = e - i, o = s > 0 ? this.#l.top / s * a : 0, l = s > 0 ? this.#l.bottom / s * a : 0, h = {
|
|
1552
1625
|
min: r ? i : i - l,
|
|
1553
|
-
max: n ? e : e +
|
|
1626
|
+
max: n ? e : e + o
|
|
1627
|
+
}, d = this.yScale.getTransform();
|
|
1628
|
+
if (d === null) return h;
|
|
1629
|
+
const m = d.inverse(h.min), c = d.inverse(h.max), f = this.yScale.isPlottable(m) && m <= d.inverse(i), g = this.yScale.isPlottable(c) && c >= d.inverse(e);
|
|
1630
|
+
return {
|
|
1631
|
+
min: f ? m : d.inverse(i),
|
|
1632
|
+
max: g ? c : d.inverse(e)
|
|
1554
1633
|
};
|
|
1555
1634
|
}
|
|
1556
1635
|
/** Which Y edges the user pinned — a pinned edge takes no padding. */
|
|
1557
|
-
#
|
|
1636
|
+
#ti() {
|
|
1558
1637
|
return {
|
|
1559
|
-
min: this.#
|
|
1560
|
-
max: this.#
|
|
1638
|
+
min: this.#Ht(this.#p.min),
|
|
1639
|
+
max: this.#Ht(this.#p.max)
|
|
1561
1640
|
};
|
|
1562
1641
|
}
|
|
1642
|
+
#Ht(t) {
|
|
1643
|
+
return t === void 0 || t === "auto" ? !1 : !(typeof t == "number" && !this.yScale.isPlottable(t));
|
|
1644
|
+
}
|
|
1563
1645
|
/**
|
|
1564
1646
|
* Shift the visible time range by `timeDelta` ms. Hard-clamped so at
|
|
1565
1647
|
* least one data point stays on screen — no rubber-band, no overshoot.
|
|
@@ -1570,18 +1652,18 @@ class ci extends ft {
|
|
|
1570
1652
|
* as a gesture for visual easing. Public so consumers can drive pan
|
|
1571
1653
|
* programmatically.
|
|
1572
1654
|
*/
|
|
1573
|
-
pan(t, i = this
|
|
1574
|
-
i > 0 && (this
|
|
1575
|
-
const e =
|
|
1576
|
-
currentLogical: this.#
|
|
1655
|
+
pan(t, i = this.#$) {
|
|
1656
|
+
i > 0 && (this.#$ = i), this.#M = !1;
|
|
1657
|
+
const e = ct({
|
|
1658
|
+
currentLogical: this.#h,
|
|
1577
1659
|
timeDelta: t,
|
|
1578
1660
|
chartWidth: i,
|
|
1579
1661
|
dataInterval: this.#r,
|
|
1580
1662
|
padding: { left: this.#l.left, right: this.#l.right },
|
|
1581
|
-
dataStart: this.#
|
|
1663
|
+
dataStart: this.#T,
|
|
1582
1664
|
dataEnd: this.#e
|
|
1583
1665
|
});
|
|
1584
|
-
e.newLogical !== null && (this.#
|
|
1666
|
+
e.newLogical !== null && (this.#m = !e.autoScrollOff, this.#C(e.newLogical, { emitChange: !0 }), e.edgeReached !== null && (this.#pt[e.edgeReached.side] = e.edgeReached.boundaryTime, this.#yt?.(e.edgeReached)), this.#R.pause(), this.#zt());
|
|
1585
1667
|
}
|
|
1586
1668
|
/**
|
|
1587
1669
|
* Zoom around a time anchor. `factor < 1` zooms in, `> 1` zooms out.
|
|
@@ -1598,37 +1680,37 @@ class ci extends ft {
|
|
|
1598
1680
|
* and produce a visible jump). Cursor-anchored zoom is intentionally
|
|
1599
1681
|
* sacrificed in follow-live mode — pan first to inspect history.
|
|
1600
1682
|
*/
|
|
1601
|
-
zoomAt(t, i, e = this
|
|
1602
|
-
e > 0 && (this
|
|
1603
|
-
const s =
|
|
1604
|
-
currentLogical: this.#
|
|
1683
|
+
zoomAt(t, i, e = this.#$) {
|
|
1684
|
+
e > 0 && (this.#$ = e), this.#M = !1;
|
|
1685
|
+
const s = dt({
|
|
1686
|
+
currentLogical: this.#h,
|
|
1605
1687
|
centerTime: t,
|
|
1606
1688
|
factor: i,
|
|
1607
1689
|
chartWidth: e,
|
|
1608
1690
|
dataInterval: this.#r,
|
|
1609
1691
|
padding: { left: this.#l.left, right: this.#l.right },
|
|
1610
|
-
dataStart: this.#
|
|
1692
|
+
dataStart: this.#T,
|
|
1611
1693
|
dataEnd: this.#e
|
|
1612
1694
|
});
|
|
1613
1695
|
if (s.newLogical === null) return;
|
|
1614
1696
|
let r = s.newLogical;
|
|
1615
|
-
if (this.#
|
|
1616
|
-
const n = r.to - r.from, a =
|
|
1617
|
-
r = { from:
|
|
1697
|
+
if (this.#m && this.#e !== null) {
|
|
1698
|
+
const n = r.to - r.from, a = B(this.#l.right, n, this.#r, e), o = this.#e + a;
|
|
1699
|
+
r = { from: o - n, to: o }, this.#E = this.#e;
|
|
1618
1700
|
}
|
|
1619
|
-
this.#C(r, { emitChange: !0 }), this.#R.pause(), this.#
|
|
1701
|
+
this.#C(r, { emitChange: !0 }), this.#R.pause(), this.#zt();
|
|
1620
1702
|
}
|
|
1621
1703
|
/**
|
|
1622
1704
|
* Common tail for `pan` / `zoomAt`: routes the just-committed `#logical`
|
|
1623
1705
|
* to the engine as a gesture so the X spring retargets and the Y spring
|
|
1624
1706
|
* eases over `animations.axis.y.gesture`.
|
|
1625
1707
|
*/
|
|
1626
|
-
#
|
|
1627
|
-
const t = this.#
|
|
1708
|
+
#zt() {
|
|
1709
|
+
const t = this.#h;
|
|
1628
1710
|
if (t.to <= t.from) return;
|
|
1629
|
-
this.#
|
|
1711
|
+
this.#W = !0, this.#g = !0;
|
|
1630
1712
|
const i = performance.now();
|
|
1631
|
-
this.#s.onPanZoom({ xTarget: t, yAuto: !0 }, i), this.#
|
|
1713
|
+
this.#s.onPanZoom({ xTarget: t, yAuto: !0 }, i), this.#b(i);
|
|
1632
1714
|
}
|
|
1633
1715
|
/**
|
|
1634
1716
|
* Tick the engine at the given wall clock and push the resolved X / Y
|
|
@@ -1643,11 +1725,11 @@ class ci extends ft {
|
|
|
1643
1725
|
* `effectiveNow === startWall` and the prune step keeps zero-duration
|
|
1644
1726
|
* events long enough for the slot processor to snap them.
|
|
1645
1727
|
*/
|
|
1646
|
-
#
|
|
1728
|
+
#b(t) {
|
|
1647
1729
|
const i = this.#s.tick(t), e = this.#a.size;
|
|
1648
1730
|
if (e.media.width === 0 || e.media.height === 0) return;
|
|
1649
1731
|
const s = e.media.height - this.xAxisHeight;
|
|
1650
|
-
this.#
|
|
1732
|
+
this.#B = this.#It({ min: i.yRange.min, max: i.yRange.max, chartHeight: s }), this.#at = i.yRange.min, this.#ot = i.yRange.max, this.syncScales();
|
|
1651
1733
|
}
|
|
1652
1734
|
/**
|
|
1653
1735
|
* Lightweight scale sync: updates timeScale/yScale from current viewport state
|
|
@@ -1663,7 +1745,7 @@ class ci extends ft {
|
|
|
1663
1745
|
i,
|
|
1664
1746
|
t.horizontalPixelRatio,
|
|
1665
1747
|
this.#r
|
|
1666
|
-
), this.yScale.update(this.#
|
|
1748
|
+
), this.yScale.update(this.#B, e, t.verticalPixelRatio);
|
|
1667
1749
|
}
|
|
1668
1750
|
/** Expensive: background, grid, all series. Only on data/viewport/resize change. */
|
|
1669
1751
|
renderMain(t) {
|
|
@@ -1671,80 +1753,74 @@ class ci extends ft {
|
|
|
1671
1753
|
if (i.media.width === 0 || i.media.height === 0) return;
|
|
1672
1754
|
const e = typeof t == "number" ? t : performance.now(), s = this.#s.tick(e);
|
|
1673
1755
|
s.animating && this.#i.markDirty();
|
|
1674
|
-
const r = s.yRange.min, n = s.yRange.max, a = r !== this.#
|
|
1756
|
+
const r = s.yRange.min, n = s.yRange.max, a = r !== this.#at || n !== this.#ot;
|
|
1675
1757
|
if (a) {
|
|
1676
1758
|
const c = i.media.height - this.xAxisHeight;
|
|
1677
|
-
this.#
|
|
1759
|
+
this.#B = this.#It({ min: r, max: n, chartHeight: c }), this.#at = r, this.#ot = n;
|
|
1678
1760
|
}
|
|
1679
|
-
if (this.syncScales(), this.#
|
|
1680
|
-
const c = this.#s.lastXTarget ?? this.#
|
|
1761
|
+
if (this.syncScales(), this.#jt(), this.#e !== null && !this.#m) {
|
|
1762
|
+
const c = this.#s.lastXTarget ?? this.#h, f = c.to - c.from;
|
|
1681
1763
|
if (f > 0) {
|
|
1682
|
-
const
|
|
1683
|
-
Math.abs(c.to - (this.#e +
|
|
1764
|
+
const g = this.#a.size.media.width - this.yAxisWidth, u = B(this.#l.right, f, this.#r, g), p = this.#r * 0.5;
|
|
1765
|
+
Math.abs(c.to - (this.#e + u)) < p && (this.#m = !0);
|
|
1684
1766
|
}
|
|
1685
1767
|
}
|
|
1686
1768
|
a && this.emit("viewportChange");
|
|
1687
|
-
let
|
|
1769
|
+
let o = !1, l = !1, h = !1;
|
|
1688
1770
|
this.#a.useMainLayer((c) => {
|
|
1689
|
-
const { context: f, bitmapSize:
|
|
1690
|
-
f.clearRect(0, 0,
|
|
1691
|
-
const
|
|
1692
|
-
|
|
1693
|
-
const w = this
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
const v = Z() ? 0 : this.#m.axis.gridMs;
|
|
1698
|
-
this.#I.setTarget(L, { now: e, duration: v });
|
|
1771
|
+
const { context: f, bitmapSize: g } = c, u = (i.media.width - this.yAxisWidth) * i.horizontalPixelRatio, p = (i.media.height - this.xAxisHeight) * i.verticalPixelRatio, S = this.#I(c);
|
|
1772
|
+
f.clearRect(0, 0, g.width, g.height), f.save(), f.translate(S, 0), f.beginPath(), f.rect(0, 0, this.#Bt(c, u), p), f.clip(), this.yScale.tickTracker.setCurrentTicks(this.yScale.niceTickValues()), this.timeScale.tickTracker.setCurrentTicks(this.timeScale.niceTickValues(this.#r).ticks), this.yScale.tickTracker.tick(e), this.timeScale.tickTracker.tick(e);
|
|
1773
|
+
const A = this.yScale.tickTracker.snapshot(), k = this.timeScale.tickTracker.snapshot();
|
|
1774
|
+
o = A.isAnimating, l = k.isAnimating, (A.isAnimating || k.isAnimating || s.animating) && this.emit("tickFrame");
|
|
1775
|
+
const w = this.#F ? 1 : 0;
|
|
1776
|
+
if ((w === 0 || A.entries.length > 0 || k.entries.length > 0) && w !== this.#H.target) {
|
|
1777
|
+
const v = J() ? 0 : this.#d.axis.gridMs;
|
|
1778
|
+
this.#H.setTarget(w, { now: e, duration: v });
|
|
1699
1779
|
}
|
|
1700
|
-
|
|
1701
|
-
const
|
|
1702
|
-
lt({
|
|
1780
|
+
h = this.#H.tick(e);
|
|
1781
|
+
const P = this.#H.current, R = {
|
|
1703
1782
|
scope: c,
|
|
1704
1783
|
timeScale: this.timeScale,
|
|
1705
1784
|
yScale: this.yScale,
|
|
1706
1785
|
theme: this.#n,
|
|
1707
|
-
yTicks:
|
|
1708
|
-
timeTicks:
|
|
1709
|
-
alpha:
|
|
1710
|
-
}
|
|
1711
|
-
|
|
1786
|
+
yTicks: A,
|
|
1787
|
+
timeTicks: k,
|
|
1788
|
+
alpha: P
|
|
1789
|
+
}, L = this.#Nt(c, u);
|
|
1790
|
+
L === 0 && $(R), this.#_t(c, u, p);
|
|
1791
|
+
const C = this.#l, _ = { top: C.top, bottom: C.bottom }, G = this.#wt(s), I = this.#k;
|
|
1712
1792
|
for (const v of this.#t) {
|
|
1713
|
-
const
|
|
1714
|
-
if (
|
|
1715
|
-
const
|
|
1793
|
+
const T = v.renderer.getAlpha?.() ?? (v.visible ? 1 : 0);
|
|
1794
|
+
if (T <= 0) continue;
|
|
1795
|
+
const z = {
|
|
1716
1796
|
scope: c,
|
|
1717
1797
|
timeScale: this.timeScale,
|
|
1718
1798
|
yScale: this.yScale,
|
|
1719
1799
|
theme: this.#n,
|
|
1720
1800
|
dataInterval: this.#r,
|
|
1721
|
-
padding:
|
|
1722
|
-
state:
|
|
1801
|
+
padding: _,
|
|
1802
|
+
state: G,
|
|
1723
1803
|
seriesId: v.id
|
|
1724
|
-
},
|
|
1725
|
-
if (
|
|
1726
|
-
const
|
|
1727
|
-
v.renderer.render(
|
|
1804
|
+
}, V = f.globalAlpha;
|
|
1805
|
+
if (T < 1 && (f.globalAlpha = V * T), I) {
|
|
1806
|
+
const U = performance.now();
|
|
1807
|
+
v.renderer.render(z), I.recordSeries(v.id, performance.now() - U, e);
|
|
1728
1808
|
} else
|
|
1729
|
-
v.renderer.render(
|
|
1730
|
-
|
|
1809
|
+
v.renderer.render(z);
|
|
1810
|
+
T < 1 && (f.globalAlpha = V);
|
|
1731
1811
|
}
|
|
1732
|
-
f.restore()
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
alpha: M
|
|
1738
|
-
}), this.#Lt(c, g), this.#Mt(c, d);
|
|
1739
|
-
}), this.#j();
|
|
1740
|
-
let m = !1;
|
|
1812
|
+
f.restore();
|
|
1813
|
+
const H = { left: S, width: u, height: p };
|
|
1814
|
+
L === 0 ? (this.#Wt({ scope: c, pane: H, timeTicks: k, alpha: P }), this.#lt(c, u)) : (this.#lt(c, u), this.#si({ grid: R, pane: H, overhang: L })), this.#Ft(c, u), this.#Vt(c, p);
|
|
1815
|
+
}), this.#it();
|
|
1816
|
+
let d = !1;
|
|
1741
1817
|
for (const c of this.#t)
|
|
1742
1818
|
if (c.renderer.needsAnimation) {
|
|
1743
|
-
|
|
1819
|
+
d = !0, this.#i.markDirty();
|
|
1744
1820
|
break;
|
|
1745
1821
|
}
|
|
1746
|
-
const
|
|
1747
|
-
(
|
|
1822
|
+
const m = o || l;
|
|
1823
|
+
(m || h) && this.#i.markDirty(), !s.animating && !d && !m && (this.yScale.tickTracker.markArmed(), this.timeScale.tickTracker.markArmed());
|
|
1748
1824
|
}
|
|
1749
1825
|
/**
|
|
1750
1826
|
* Erase the main layer's alpha across the top fade zone so content
|
|
@@ -1753,12 +1829,12 @@ class ci extends ft {
|
|
|
1753
1829
|
* a paint-over — so the container background behind the canvas (solid or
|
|
1754
1830
|
* CSS gradient) shows through untouched.
|
|
1755
1831
|
*/
|
|
1756
|
-
#
|
|
1757
|
-
if (this.#
|
|
1758
|
-
const e = Math.min(Math.round(this.#
|
|
1832
|
+
#Vt(t, i) {
|
|
1833
|
+
if (this.#A.top <= 0) return;
|
|
1834
|
+
const e = Math.min(Math.round(this.#A.top * t.verticalPixelRatio), Math.round(i));
|
|
1759
1835
|
if (e < 1) return;
|
|
1760
1836
|
const { context: s } = t;
|
|
1761
|
-
s.save(), s.globalCompositeOperation = "destination-out", s.fillStyle = this.#
|
|
1837
|
+
s.save(), s.globalCompositeOperation = "destination-out", s.fillStyle = this.#ni(s, e), s.fillRect(0, 0, t.bitmapSize.width, e), s.restore();
|
|
1762
1838
|
}
|
|
1763
1839
|
/**
|
|
1764
1840
|
* Run the vertical gridlines a short stub past the pane floor, dissolving
|
|
@@ -1768,21 +1844,23 @@ class ci extends ft {
|
|
|
1768
1844
|
* nothing else paints) and faded with the same destination-out ramp the
|
|
1769
1845
|
* edge masks use, so the taper stays correct over any background.
|
|
1770
1846
|
*/
|
|
1771
|
-
#
|
|
1772
|
-
const { scope: i,
|
|
1773
|
-
if (!this.#F &&
|
|
1774
|
-
const { context:
|
|
1775
|
-
if (
|
|
1776
|
-
const
|
|
1777
|
-
|
|
1778
|
-
const
|
|
1779
|
-
for (const { value:
|
|
1780
|
-
const
|
|
1781
|
-
if (
|
|
1782
|
-
const
|
|
1783
|
-
|
|
1847
|
+
#Wt(t) {
|
|
1848
|
+
const { scope: i, pane: e, timeTicks: s, alpha: r } = t;
|
|
1849
|
+
if (!this.#F && r <= 0.01 || this.xAxisHeight === 0) return;
|
|
1850
|
+
const { context: n, horizontalPixelRatio: a, verticalPixelRatio: o } = i, l = Math.round(e.height), h = Math.round(D * o);
|
|
1851
|
+
if (h < 1) return;
|
|
1852
|
+
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 = l, n.lineWidth = Ct(a);
|
|
1854
|
+
const m = It(a), c = Math.round(e.width);
|
|
1855
|
+
for (const { value: f, opacity: g } of s.entries) {
|
|
1856
|
+
const u = g * r;
|
|
1857
|
+
if (u <= 0.01) continue;
|
|
1858
|
+
const p = Math.round(this.timeScale.timeToBitmapX(f)) + m;
|
|
1859
|
+
if (p < 0 || p > c) continue;
|
|
1860
|
+
const S = e.left + p;
|
|
1861
|
+
n.globalAlpha = u, n.beginPath(), n.moveTo(S, l), n.lineTo(S, l + h), n.stroke();
|
|
1784
1862
|
}
|
|
1785
|
-
|
|
1863
|
+
n.restore(), this.#Ot(i, e.height);
|
|
1786
1864
|
}
|
|
1787
1865
|
/**
|
|
1788
1866
|
* Erase ramp over the below-pane overrun strip — the gridline tail stubs
|
|
@@ -1790,16 +1868,16 @@ class ci extends ft {
|
|
|
1790
1868
|
* erased at the pane floor, total at the strip's bottom edge, so whatever
|
|
1791
1869
|
* crosses the floor tapers out short of the time-axis labels.
|
|
1792
1870
|
*/
|
|
1793
|
-
#
|
|
1871
|
+
#Ot(t, i) {
|
|
1794
1872
|
if (this.xAxisHeight === 0) return;
|
|
1795
|
-
const e = Math.round(i), s = Math.round(
|
|
1873
|
+
const e = Math.round(i), s = Math.round(D * t.verticalPixelRatio);
|
|
1796
1874
|
if (s < 1) return;
|
|
1797
1875
|
const { context: r } = t;
|
|
1798
|
-
r.save(), r.globalCompositeOperation = "destination-out", r.fillStyle = this
|
|
1876
|
+
r.save(), r.globalCompositeOperation = "destination-out", r.fillStyle = this.#oi({ context: r, start: e, height: s }), r.fillRect(0, e, t.bitmapSize.width, s), r.restore();
|
|
1799
1877
|
}
|
|
1800
1878
|
/**
|
|
1801
1879
|
* Crosshair hairlines with the same edge melt the main layer runs: the
|
|
1802
|
-
* pane clip widens by the
|
|
1880
|
+
* pane clip widens by the axis-side fade intrusion and by a gridline-tail's
|
|
1803
1881
|
* overrun below the floor, then the shared erase ramps dissolve the
|
|
1804
1882
|
* overhangs — the vertical line tapers toward the time-axis labels in
|
|
1805
1883
|
* step with the gridline stubs and the horizontal line rides under the
|
|
@@ -1807,104 +1885,177 @@ class ci extends ft {
|
|
|
1807
1885
|
* edge. Erases alpha (destination-out), so it must run while the
|
|
1808
1886
|
* crosshair is the only content on the layer.
|
|
1809
1887
|
*/
|
|
1810
|
-
#
|
|
1888
|
+
#ii(t) {
|
|
1811
1889
|
const i = this.#c;
|
|
1812
1890
|
if (i === null) return;
|
|
1813
|
-
const { scope: e, chartBitmapWidth: s, chartBitmapHeight: r } = t, { context: n } = e, a = this.#
|
|
1814
|
-
n.save(), n.beginPath(), n.rect(
|
|
1891
|
+
const { scope: e, chartBitmapWidth: s, chartBitmapHeight: r } = t, { context: n } = e, a = this.#I(e), o = this.#Bt(e, s), l = this.#Nt(e, s), h = this.xAxisHeight > 0 ? Math.round(D * e.verticalPixelRatio) : 0;
|
|
1892
|
+
n.save(), n.translate(a, 0), n.beginPath(), n.rect(-l, 0, o + l, r + h), n.clip(), vt({
|
|
1815
1893
|
scope: e,
|
|
1816
|
-
bitmapX: i.mediaX * e.horizontalPixelRatio,
|
|
1894
|
+
bitmapX: i.mediaX * e.horizontalPixelRatio - a,
|
|
1817
1895
|
bitmapY: i.mediaY * e.verticalPixelRatio,
|
|
1818
1896
|
theme: this.#n,
|
|
1819
|
-
pane: { width: s, height: r }
|
|
1820
|
-
|
|
1897
|
+
pane: { width: s, height: r },
|
|
1898
|
+
leftOverhang: l
|
|
1899
|
+
}), n.restore(), this.#Ot(e, r), l === 0 ? this.#lt(e, s) : this.#Et(e, l), this.#Ft(e, s), this.#Vt(e, r);
|
|
1821
1900
|
}
|
|
1822
1901
|
/**
|
|
1823
|
-
* Horizontal
|
|
1824
|
-
* rides under the Y-axis column
|
|
1825
|
-
*
|
|
1826
|
-
* pane edge. Same destination-out erase — background-safe over gradients.
|
|
1902
|
+
* Horizontal companion to {@link #applyTopFade}: dissolve content that
|
|
1903
|
+
* rides under the Y-axis column (the pane clip is widened by the same
|
|
1904
|
+
* zone). Same destination-out erase — background-safe over gradients.
|
|
1827
1905
|
* Runs the full bitmap height so the below-pane gridline tail stubs melt
|
|
1828
1906
|
* at the edges in step with their lines above.
|
|
1829
1907
|
*/
|
|
1830
|
-
#
|
|
1831
|
-
const
|
|
1832
|
-
|
|
1833
|
-
const
|
|
1834
|
-
|
|
1835
|
-
}
|
|
1836
|
-
/**
|
|
1837
|
-
*
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1908
|
+
#lt(t, i) {
|
|
1909
|
+
const e = this.#ct(t, i);
|
|
1910
|
+
if (e === null) return;
|
|
1911
|
+
const { context: s } = t, r = Math.min(e.inner, e.outer), n = Math.abs(e.outer - e.inner);
|
|
1912
|
+
s.save(), s.globalCompositeOperation = "destination-out", s.fillStyle = this.#ai({ context: s, zone: e }), s.fillRect(r, 0, n, t.bitmapSize.height), s.restore();
|
|
1913
|
+
}
|
|
1914
|
+
/** Dissolve content leaving through a plain canvas edge — the side opposite
|
|
1915
|
+
* the Y axis, or either side on a spatial-only chart — when configured. */
|
|
1916
|
+
#Ft(t, i) {
|
|
1917
|
+
const { context: e } = t;
|
|
1918
|
+
for (const s of this.#ei()) {
|
|
1919
|
+
const r = this.#ri({ scope: t, paneBitmapWidth: i, side: s });
|
|
1920
|
+
if (r === null) continue;
|
|
1921
|
+
const n = Math.min(r.edge, r.inner), a = Math.abs(r.inner - r.edge);
|
|
1922
|
+
e.save(), e.globalCompositeOperation = "destination-out", e.fillStyle = this.#li({ context: e, side: s, zone: r }), e.fillRect(n, 0, a, t.bitmapSize.height), e.restore();
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
/** Sides whose fade is a plain edge zone rather than the axis-column ramp. */
|
|
1926
|
+
#ei() {
|
|
1927
|
+
return this.#X() ? Ot : this.yAxisPosition === "left" ? Nt : Ft;
|
|
1928
|
+
}
|
|
1929
|
+
/** Bitmap px the gridlines and crosshair run under a left Y-axis column, where
|
|
1930
|
+
* data can't follow (see {@link #axisFadeZone}). `0` on the right or with the fade off. */
|
|
1931
|
+
#Nt(t, i) {
|
|
1932
|
+
return this.yAxisPosition !== "left" || this.#ct(t, i) === null ? 0 : Math.min(Math.round(X * t.horizontalPixelRatio), this.#I(t));
|
|
1933
|
+
}
|
|
1934
|
+
/** Left-axis grid pass, run after the axis fade: `destination-over` keeps the
|
|
1935
|
+
* gridlines out of the data ramp, then they taper out under the column. */
|
|
1936
|
+
#si(t) {
|
|
1937
|
+
const { grid: i, pane: e, overhang: s } = t, { scope: r } = i, { context: n } = r;
|
|
1938
|
+
n.save(), n.globalCompositeOperation = "destination-over", n.save(), n.translate(e.left, 0), n.beginPath(), n.rect(-s, 0, e.width + s, e.height), n.clip(), $({ ...i, leftOverhang: s }), n.restore(), this.#Wt({ scope: r, pane: e, timeTicks: i.timeTicks, alpha: i.alpha ?? 1 }), n.restore(), this.#Et(r, s);
|
|
1939
|
+
}
|
|
1940
|
+
/** Erase the {@link #gridOverhang} strip: none at the pane edge, total at its outer end. */
|
|
1941
|
+
#Et(t, i) {
|
|
1942
|
+
const e = this.#I(t), s = e - i, { context: r } = t;
|
|
1943
|
+
r.save(), r.globalCompositeOperation = "destination-out", r.fillStyle = this.#hi({ context: r, start: s, end: e }), r.fillRect(s, 0, i, t.bitmapSize.height), r.restore();
|
|
1944
|
+
}
|
|
1945
|
+
/** Canvas bitmap x of the pane's left edge — whole device pixels, so the
|
|
1946
|
+
* translated pane keeps crisp strokes. */
|
|
1947
|
+
#I(t) {
|
|
1948
|
+
return Math.round(this.plotLeft * t.horizontalPixelRatio);
|
|
1949
|
+
}
|
|
1950
|
+
/** Pane-local width of the pane clip: the pane plus the axis-side fade
|
|
1951
|
+
* intrusion, so content can slide under a right Y-axis column before the
|
|
1952
|
+
* mask dissolves it. */
|
|
1953
|
+
#Bt(t, i) {
|
|
1954
|
+
const e = this.#ct(t, i)?.intrusion ?? 0;
|
|
1955
|
+
return i + e;
|
|
1956
|
+
}
|
|
1957
|
+
/**
|
|
1958
|
+
* Bitmap geometry of the fade zone on the Y-axis side. On the right the
|
|
1959
|
+
* ramp finishes just inside the column — {@link AXIS_FADE_END_GAP_PX} past
|
|
1960
|
+
* the pane edge, still short of where the label glyphs start — so content
|
|
1961
|
+
* is fully erased before it can cross any axis text; everything wider
|
|
1962
|
+
* spills back into the pane as the soft lead-in. On the left it finishes
|
|
1963
|
+
* at the pane edge. Erasing starts at `inner` and is total at `outer`;
|
|
1964
|
+
* `intrusion` is what the pane clip extends by (content past the ramp is
|
|
1965
|
+
* total-erased anyway). `null` when the axis is hidden, its side's fade
|
|
1966
|
+
* is `0`, or the chart is spatial-only.
|
|
1967
|
+
*/
|
|
1968
|
+
#ct(t, i) {
|
|
1969
|
+
if (this.#X()) return null;
|
|
1970
|
+
const e = this.yAxisPosition === "left", s = t.bitmapSize.width, r = e ? this.#I(t) : Math.max(0, Math.round(s - i));
|
|
1971
|
+
if (r === 0) return null;
|
|
1972
|
+
const n = t.horizontalPixelRatio, a = Math.round(X * n), o = Math.round(Wt * n), l = e ? 0 : Math.min(a, r), h = e ? a + o : l + o, d = e ? this.#A.left : this.#A.right, m = d === null ? h : Math.round(d * n);
|
|
1973
|
+
if (m < 1) return null;
|
|
1974
|
+
if (e) {
|
|
1975
|
+
const g = r, u = Math.min(m, s - g);
|
|
1976
|
+
return { inner: g + u, outer: g, intrusion: l };
|
|
1977
|
+
}
|
|
1978
|
+
const c = i + l, f = Math.min(m, c);
|
|
1979
|
+
return { inner: c - f, outer: c, intrusion: l };
|
|
1980
|
+
}
|
|
1981
|
+
/** Bitmap geometry of a plain fade at the `side` canvas edge — total erase
|
|
1982
|
+
* at `edge`, none at `inner`. Off unless that side's fade is configured. */
|
|
1983
|
+
#ri(t) {
|
|
1984
|
+
const { scope: i, paneBitmapWidth: e, side: s } = t, r = this.#A[s];
|
|
1985
|
+
if (r === null) return null;
|
|
1986
|
+
const n = Math.min(Math.round(r * i.horizontalPixelRatio), Math.round(e));
|
|
1987
|
+
if (n < 1) return null;
|
|
1988
|
+
if (s === "right") {
|
|
1989
|
+
const a = i.bitmapSize.width;
|
|
1990
|
+
return { edge: a, inner: a - n };
|
|
1991
|
+
}
|
|
1992
|
+
return { edge: 0, inner: n };
|
|
1853
1993
|
}
|
|
1854
1994
|
/** Memoized mask gradient — rebuilt only when the bitmap zone height
|
|
1855
1995
|
* changes (resize, DPR change, `setFade`). Ease-out ramp (`1 - t²`):
|
|
1856
1996
|
* near-total erasure across the upper half keeps a floating header
|
|
1857
1997
|
* legible, then a soft tail into full opacity at the zone's bottom. */
|
|
1858
|
-
#
|
|
1859
|
-
const e = this.#
|
|
1998
|
+
#ni(t, i) {
|
|
1999
|
+
const e = this.#v.top;
|
|
1860
2000
|
if (e !== null && e.height === i) return e.gradient;
|
|
1861
2001
|
const s = t.createLinearGradient(0, 0, 0, i);
|
|
1862
2002
|
for (let r = 0; r <= 4; r++) {
|
|
1863
2003
|
const n = r / 4;
|
|
1864
2004
|
s.addColorStop(n, `rgba(0, 0, 0, ${1 - n * n})`);
|
|
1865
2005
|
}
|
|
1866
|
-
return this.#
|
|
2006
|
+
return this.#v.top = { height: i, gradient: s }, s;
|
|
1867
2007
|
}
|
|
1868
|
-
/**
|
|
2008
|
+
/** Axis-column mask — erase grows from 0 at the ramp's inner start to
|
|
1869
2009
|
* total at its outer end on an ease-in (`s³`): zero slope *and* zero
|
|
1870
2010
|
* curvature at the entry, so the in-pane lead-in barely dims the newest
|
|
1871
2011
|
* candles at rest and the aggressive erasing all happens deep under the
|
|
1872
2012
|
* axis column. Nine stops keep the piecewise interpolation smooth across
|
|
1873
2013
|
* the zone. */
|
|
1874
|
-
#
|
|
1875
|
-
const i = this.#
|
|
1876
|
-
if (
|
|
1877
|
-
const
|
|
1878
|
-
for (let
|
|
1879
|
-
const
|
|
1880
|
-
|
|
2014
|
+
#ai(t) {
|
|
2015
|
+
const { context: i, zone: e } = t, s = this.#v.axis;
|
|
2016
|
+
if (s !== null && s.inner === e.inner && s.outer === e.outer) return s.gradient;
|
|
2017
|
+
const r = i.createLinearGradient(e.inner, 0, e.outer, 0);
|
|
2018
|
+
for (let n = 0; n <= 8; n++) {
|
|
2019
|
+
const a = n / 8;
|
|
2020
|
+
r.addColorStop(a, `rgba(0, 0, 0, ${a * a * a})`);
|
|
1881
2021
|
}
|
|
1882
|
-
return this.#
|
|
2022
|
+
return this.#v.axis = { inner: e.inner, outer: e.outer, gradient: r }, r;
|
|
1883
2023
|
}
|
|
1884
2024
|
/** Erase ramp for the gridline tail stubs — nothing erased at the pane
|
|
1885
2025
|
* floor, total at the stub tip, ease-in (`t²`) so a stub holds strength
|
|
1886
2026
|
* briefly before melting away short of the label glyphs. */
|
|
1887
|
-
|
|
1888
|
-
const i = this.#
|
|
2027
|
+
#oi(t) {
|
|
2028
|
+
const i = this.#v.tail;
|
|
1889
2029
|
if (i !== null && i.start === t.start && i.height === t.height) return i.gradient;
|
|
1890
2030
|
const e = t.context.createLinearGradient(0, t.start, 0, t.start + t.height);
|
|
1891
2031
|
for (let s = 0; s <= 4; s++) {
|
|
1892
2032
|
const r = s / 4;
|
|
1893
2033
|
e.addColorStop(r, `rgba(0, 0, 0, ${r * r})`);
|
|
1894
2034
|
}
|
|
1895
|
-
return this.#
|
|
1896
|
-
}
|
|
1897
|
-
/**
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
2035
|
+
return this.#v.tail = { start: t.start, height: t.height, gradient: e }, e;
|
|
2036
|
+
}
|
|
2037
|
+
/** Gridline overhang ramp — ease-in (`t²`) like the tail stubs. */
|
|
2038
|
+
#hi(t) {
|
|
2039
|
+
const i = this.#v.overhang;
|
|
2040
|
+
if (i !== null && i.start === t.start && i.end === t.end) return i.gradient;
|
|
2041
|
+
const e = t.context.createLinearGradient(t.end, 0, t.start, 0);
|
|
2042
|
+
for (let s = 0; s <= 4; s++) {
|
|
2043
|
+
const r = s / 4;
|
|
2044
|
+
e.addColorStop(r, `rgba(0, 0, 0, ${r * r})`);
|
|
2045
|
+
}
|
|
2046
|
+
return this.#v.overhang = { start: t.start, end: t.end, gradient: e }, e;
|
|
2047
|
+
}
|
|
2048
|
+
/** Opposite-edge mask — mirror of the top ramp: total erase at the
|
|
2049
|
+
* canvas edge, fully opaque at the zone's inner boundary. */
|
|
2050
|
+
#li(t) {
|
|
2051
|
+
const { context: i, side: e, zone: s } = t, r = this.#v.edge[e];
|
|
2052
|
+
if (r !== null && r.edge === s.edge && r.inner === s.inner) return r.gradient;
|
|
2053
|
+
const n = i.createLinearGradient(s.edge, 0, s.inner, 0);
|
|
2054
|
+
for (let a = 0; a <= 4; a++) {
|
|
2055
|
+
const o = a / 4;
|
|
2056
|
+
n.addColorStop(o, `rgba(0, 0, 0, ${1 - o * o})`);
|
|
1906
2057
|
}
|
|
1907
|
-
return this.#
|
|
2058
|
+
return this.#v.edge[e] = { edge: s.edge, inner: s.inner, gradient: n }, n;
|
|
1908
2059
|
}
|
|
1909
2060
|
/**
|
|
1910
2061
|
* Paint a full frame: the main layer, then the overlay synchronously on
|
|
@@ -1915,13 +2066,13 @@ class ci extends ft {
|
|
|
1915
2066
|
* `markDirty` calls made *during* `renderOverlay` (pulse, edge spinner)
|
|
1916
2067
|
* land after the consume and re-arm cleanly for the next frame.
|
|
1917
2068
|
*/
|
|
1918
|
-
#
|
|
1919
|
-
this.#
|
|
2069
|
+
#dt(t) {
|
|
2070
|
+
this.#ut("main", t, () => this.renderMain(t)), this.#o.consume(), this.#ut("overlay", t, () => this.renderOverlay(t));
|
|
1920
2071
|
}
|
|
1921
2072
|
/** Run a layer's paint, booking duration + draw calls to the perf monitor
|
|
1922
2073
|
* when one is attached — keeps overlay time out of the `main` samples. */
|
|
1923
|
-
#
|
|
1924
|
-
const s = this.#
|
|
2074
|
+
#ut(t, i, e) {
|
|
2075
|
+
const s = this.#k;
|
|
1925
2076
|
if (!s) {
|
|
1926
2077
|
e();
|
|
1927
2078
|
return;
|
|
@@ -1935,74 +2086,74 @@ class ci extends ft {
|
|
|
1935
2086
|
const i = this.#a.size;
|
|
1936
2087
|
if (i.media.width === 0 || i.media.height === 0) return;
|
|
1937
2088
|
let e = !1;
|
|
1938
|
-
for (const
|
|
1939
|
-
if (
|
|
2089
|
+
for (const h of this.#t)
|
|
2090
|
+
if (h.visible && h.renderer.overlayNeedsAnimation) {
|
|
1940
2091
|
e = !0;
|
|
1941
2092
|
break;
|
|
1942
2093
|
}
|
|
1943
|
-
this.#u.some((
|
|
2094
|
+
this.#u.some((h) => h.pulse) && (e = !0);
|
|
1944
2095
|
const s = {
|
|
1945
|
-
left: this.#
|
|
1946
|
-
right: this.#
|
|
2096
|
+
left: this.#z.left && this.#S.left === "loading" ? "idle" : this.#S.left,
|
|
2097
|
+
right: this.#z.right && this.#S.right === "loading" ? "idle" : this.#S.right
|
|
1947
2098
|
}, r = {
|
|
1948
|
-
left: this.#
|
|
1949
|
-
right: this.#
|
|
1950
|
-
}, n = r.left !== null || r.right !== null, a = s.left === "loading" || s.right === "loading" || n,
|
|
1951
|
-
if (!(!l && !this.#
|
|
1952
|
-
if (this.#
|
|
2099
|
+
left: this.#Dt("left"),
|
|
2100
|
+
right: this.#Dt("right")
|
|
2101
|
+
}, n = r.left !== null || r.right !== null, a = s.left === "loading" || s.right === "loading" || n, o = s.left !== "idle" || s.right !== "idle" || n, l = !!this.#c || e || o || this.#u.length > 0 || this.#y.length > 0;
|
|
2102
|
+
if (!(!l && !this.#gt)) {
|
|
2103
|
+
if (this.#gt = l, this.#a.useOverlayLayer((h) => {
|
|
1953
2104
|
if (!l) return;
|
|
1954
|
-
const
|
|
1955
|
-
this.#c && this.#
|
|
1956
|
-
const c = this.#l, f = { top: c.top, bottom: c.bottom },
|
|
1957
|
-
for (const
|
|
1958
|
-
|
|
1959
|
-
scope:
|
|
2105
|
+
const d = (i.media.width - this.yAxisWidth) * i.horizontalPixelRatio, m = (i.media.height - this.xAxisHeight) * i.verticalPixelRatio;
|
|
2106
|
+
this.#c && this.#Mt() && this.#ii({ scope: h, chartBitmapWidth: d, chartBitmapHeight: m }), h.context.save(), h.context.translate(this.#I(h), 0), h.context.beginPath(), h.context.rect(0, 0, d, m), h.context.clip();
|
|
2107
|
+
const c = this.#l, f = { top: c.top, bottom: c.bottom }, g = this.getAnimationState();
|
|
2108
|
+
for (const u of this.#t)
|
|
2109
|
+
u.visible && u.renderer.drawOverlay?.({
|
|
2110
|
+
scope: h,
|
|
1960
2111
|
timeScale: this.timeScale,
|
|
1961
2112
|
yScale: this.yScale,
|
|
1962
2113
|
theme: this.#n,
|
|
1963
2114
|
dataInterval: this.#r,
|
|
1964
2115
|
padding: f,
|
|
1965
2116
|
crosshair: this.#c,
|
|
1966
|
-
state:
|
|
1967
|
-
seriesId:
|
|
2117
|
+
state: g,
|
|
2118
|
+
seriesId: u.id
|
|
1968
2119
|
});
|
|
1969
|
-
this.#
|
|
1970
|
-
scope:
|
|
2120
|
+
this.#Gt(h, d, m), this.#Zt(h, d, m), o && et({
|
|
2121
|
+
scope: h,
|
|
1971
2122
|
chartMediaWidth: i.media.width - this.yAxisWidth,
|
|
1972
2123
|
chartMediaHeight: i.media.height - this.xAxisHeight,
|
|
1973
2124
|
timeScale: this.timeScale,
|
|
1974
2125
|
theme: this.#n,
|
|
1975
2126
|
edgeStates: s,
|
|
1976
2127
|
edgeExitFades: r,
|
|
1977
|
-
edgeIndicatorFns: this.#
|
|
1978
|
-
resolveBoundary: (
|
|
1979
|
-
resolveEdgeAnchor: (
|
|
1980
|
-
resolveEdgeBarSpacing: () => this.#
|
|
1981
|
-
reportPlaceholders: (
|
|
1982
|
-
this.#
|
|
2128
|
+
edgeIndicatorFns: this.#vt,
|
|
2129
|
+
resolveBoundary: (u) => st(u, this.#pt[u], this.getDataBounds()),
|
|
2130
|
+
resolveEdgeAnchor: (u, p) => this.#Ut(u, p),
|
|
2131
|
+
resolveEdgeBarSpacing: () => this.#Kt(),
|
|
2132
|
+
reportPlaceholders: (u, p) => {
|
|
2133
|
+
this.#j[u] = { bars: p, at: performance.now() };
|
|
1983
2134
|
}
|
|
1984
|
-
}),
|
|
2135
|
+
}), h.context.restore();
|
|
1985
2136
|
}), e) {
|
|
1986
|
-
const { from:
|
|
1987
|
-
let
|
|
2137
|
+
const { from: h, to: d } = this.timeScale.getRange();
|
|
2138
|
+
let m = !1;
|
|
1988
2139
|
for (const c of this.#t)
|
|
1989
|
-
if (!(!c.visible || !c.renderer.overlayNeedsAnimation) && (c.renderer.hasOverlayContentInRange?.(
|
|
1990
|
-
|
|
2140
|
+
if (!(!c.visible || !c.renderer.overlayNeedsAnimation) && (c.renderer.hasOverlayContentInRange?.(h, d) ?? !0)) {
|
|
2141
|
+
m = !0;
|
|
1991
2142
|
break;
|
|
1992
2143
|
}
|
|
1993
|
-
if (!
|
|
2144
|
+
if (!m) {
|
|
1994
2145
|
for (const c of this.#u)
|
|
1995
|
-
if (c.pulse && c.time >=
|
|
1996
|
-
|
|
2146
|
+
if (c.pulse && c.time >= h && c.time <= d) {
|
|
2147
|
+
m = !0;
|
|
1997
2148
|
break;
|
|
1998
2149
|
}
|
|
1999
2150
|
}
|
|
2000
|
-
|
|
2151
|
+
m && this.#o.markDirty();
|
|
2001
2152
|
}
|
|
2002
|
-
a && this.#
|
|
2153
|
+
a && this.#o.markDirty();
|
|
2003
2154
|
}
|
|
2004
2155
|
}
|
|
2005
2156
|
}
|
|
2006
2157
|
export {
|
|
2007
|
-
|
|
2158
|
+
Ai as ChartInstance
|
|
2008
2159
|
};
|