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