@wick-charts/vue 0.5.1 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -6
- package/dist/BarSeries.vue.js +26 -20
- package/dist/CandlestickSeries.vue.js +30 -24
- package/dist/ChartContainer.vue.js +125 -72
- package/dist/HeatmapSeries.vue.js +40 -0
- package/dist/HeatmapSeries.vue2.js +4 -0
- package/dist/LineSeries.vue.js +26 -20
- package/dist/PieSeries.vue.js +24 -18
- package/dist/core/dist/animation/animator.js +21 -21
- package/dist/core/dist/animation/config.js +114 -87
- package/dist/core/dist/animation/easing.js +3 -1
- package/dist/core/dist/animation/intro-wave.js +68 -0
- package/dist/core/dist/axis/dom-labels.js +13 -14
- package/dist/core/dist/chart/badge-animator.js +56 -0
- package/dist/core/dist/chart/edge-indicators.js +28 -13
- package/dist/core/dist/chart/last-value.js +18 -17
- package/dist/core/dist/chart/options.js +27 -16
- package/dist/core/dist/chart/y-target.js +13 -12
- package/dist/core/dist/chart.js +868 -441
- package/dist/core/dist/components/annotation-chip.js +28 -0
- package/dist/core/dist/components/crosshair.js +8 -6
- package/dist/core/dist/components/edge-indicator.js +58 -26
- package/dist/core/dist/components/loading-indicator.js +46 -0
- package/dist/core/dist/components/marker.js +49 -31
- package/dist/core/dist/components/reference-line.js +27 -36
- package/dist/core/dist/components/time-region.js +30 -28
- package/dist/core/dist/data/store.js +38 -19
- package/dist/core/dist/data/sync.js +60 -38
- package/dist/core/dist/heatmap-tooltip.js +16 -0
- package/dist/core/dist/interactions/handler.js +86 -46
- package/dist/core/dist/perf/perf-hud.js +5 -5
- package/dist/core/dist/scales/x-scale.js +85 -37
- package/dist/core/dist/scales/y-scale.js +38 -27
- package/dist/core/dist/series/bar-intro.js +18 -0
- package/dist/core/dist/series/bar.js +337 -205
- package/dist/core/dist/series/base-multi-layer.js +224 -96
- package/dist/core/dist/series/candlestick-intro.js +57 -0
- package/dist/core/dist/series/candlestick.js +454 -276
- package/dist/core/dist/series/heatmap.js +387 -0
- package/dist/core/dist/series/line-intro.js +85 -0
- package/dist/core/dist/series/line.js +590 -282
- package/dist/core/dist/series/painters/builtins.js +2 -2
- package/dist/core/dist/series/pie.js +396 -259
- package/dist/core/dist/series/register-builtins.js +8 -7
- package/dist/core/dist/series/types.js +6 -0
- package/dist/core/dist/series/wave-intro.js +26 -0
- package/dist/core/dist/theme/create.js +69 -63
- package/dist/core/dist/theme/themes/mint-breeze.js +2 -2
- package/dist/core/dist/theme/themes/sand-dune.js +2 -2
- package/dist/core/dist/utils/color.js +109 -34
- package/dist/core/dist/utils/deep-equal.js +18 -0
- package/dist/core/dist/utils/time.js +25 -19
- package/dist/index.d.ts +1494 -122
- package/dist/index.js +177 -130
- package/dist/ui/Crosshair.vue.js +20 -21
- package/dist/ui/HeatmapTooltip.vue.js +96 -0
- package/dist/ui/HeatmapTooltip.vue2.js +4 -0
- package/dist/ui/InfoBar.vue.js +46 -47
- package/dist/ui/Legend.vue.js +80 -79
- package/dist/ui/PieLegend.vue.js +5 -5
- package/dist/ui/PieTooltip.vue.js +77 -67
- package/dist/ui/Tooltip.vue.js +60 -60
- package/dist/ui/YAxis.vue.js +2 -2
- package/dist/ui/YLabel.vue.js +94 -61
- package/package.json +2 -2
- package/src/BarSeries.vue +23 -7
- package/src/CandlestickSeries.vue +23 -7
- package/src/ChartContainer.vue +250 -13
- package/src/HeatmapSeries.vue +69 -0
- package/src/LineSeries.vue +23 -7
- package/src/PieSeries.vue +22 -6
- package/src/index.ts +84 -3
- package/src/ui/Crosshair.vue +1 -2
- package/src/ui/HeatmapTooltip.vue +144 -0
- package/src/ui/InfoBar.vue +1 -2
- package/src/ui/Legend.vue +13 -0
- package/src/ui/Marker.vue +1 -1
- package/src/ui/PieTooltip.vue +10 -0
- package/src/ui/ReferenceLine.vue +2 -2
- package/src/ui/TimeRegion.vue +2 -2
- package/src/ui/Tooltip.vue +2 -2
- package/src/ui/YLabel.vue +123 -8
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
import { DEFAULT_HEATMAP_UPDATE as Q, DEFAULT_HEATMAP_ENTRY as q } from "../animation/config.js";
|
|
2
|
+
import { easeOutCubic as J } from "../animation/easing.js";
|
|
3
|
+
import { resolveAxisFontSize as Z, resolveAxisTextColor as tt } from "../theme/resolve.js";
|
|
4
|
+
import { sampleRamp as Y, deriveHeatmapRamp as st, mixColors as et, isLightColor as it } from "../utils/color.js";
|
|
5
|
+
import { formatCompact as ot } from "../utils/format.js";
|
|
6
|
+
import { clamp as v, smoothToward as z } from "../utils/math.js";
|
|
7
|
+
import { fillRoundedRect as nt } from "./painters/canvas-path.js";
|
|
8
|
+
const E = {
|
|
9
|
+
fontSize: 10
|
|
10
|
+
};
|
|
11
|
+
function ht(r, s) {
|
|
12
|
+
return Number.isFinite(r) ? Math.abs(r) >= 1e3 ? ot(r) : s <= 2 ? r.toFixed(2) : s <= 20 ? r.toFixed(1) : Math.round(r).toString() : "—";
|
|
13
|
+
}
|
|
14
|
+
const lt = {
|
|
15
|
+
gap: 2,
|
|
16
|
+
cornerRadius: 3,
|
|
17
|
+
axisLabels: !0,
|
|
18
|
+
cellLabels: !1,
|
|
19
|
+
entryMs: q,
|
|
20
|
+
updateMs: Q
|
|
21
|
+
};
|
|
22
|
+
function rt(r) {
|
|
23
|
+
return r === void 0 || r === !1 ? !1 : r === !0 ? E : { ...E, ...r };
|
|
24
|
+
}
|
|
25
|
+
function P(r, s) {
|
|
26
|
+
const t = {
|
|
27
|
+
...r,
|
|
28
|
+
...s,
|
|
29
|
+
cellLabels: s.cellLabels === void 0 ? r.cellLabels : rt(s.cellLabels)
|
|
30
|
+
};
|
|
31
|
+
return t.gap = Math.max(0, t.gap), t.cornerRadius = Math.max(0, t.cornerRadius), t.entryMs = Math.max(0, t.entryMs), t.updateMs = Math.max(0, t.updateMs), t;
|
|
32
|
+
}
|
|
33
|
+
function O(r) {
|
|
34
|
+
const { bitmapWidth: s, bitmapHeight: t, gutterLeft: e, gutterBottom: o, cols: h, rows: i } = r;
|
|
35
|
+
if (h <= 0 || i <= 0) return null;
|
|
36
|
+
const n = Math.min(Math.max(0, r.padTop), t), l = Math.min(Math.max(0, r.padBottom), t - n), a = Math.max(0, s - e), u = Math.max(0, t - n - l - o);
|
|
37
|
+
return a <= 0 || u <= 0 ? null : {
|
|
38
|
+
originX: e,
|
|
39
|
+
originY: n,
|
|
40
|
+
slotW: a / h,
|
|
41
|
+
slotH: u / i
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function at(r, s) {
|
|
45
|
+
return `${r}\0${s}`;
|
|
46
|
+
}
|
|
47
|
+
class ct {
|
|
48
|
+
kind = "heatmap";
|
|
49
|
+
#c = [];
|
|
50
|
+
#t;
|
|
51
|
+
/** Column keys in display order (left → right). */
|
|
52
|
+
#e = [];
|
|
53
|
+
/** Row keys in display order (top → bottom). */
|
|
54
|
+
#i = [];
|
|
55
|
+
/** Cells with resolved grid coordinates. Last datum wins on (x, y) collisions. */
|
|
56
|
+
#s = [];
|
|
57
|
+
/** Grid-position → index into {@link #cells}, or -1 for an empty slot. */
|
|
58
|
+
#h = [];
|
|
59
|
+
/**
|
|
60
|
+
* Animated value per cell key. Targets live in {@link #cells}; these chase
|
|
61
|
+
* them when a same-shape `setData` lands, so live matrix updates morph
|
|
62
|
+
* colors instead of snapping.
|
|
63
|
+
*/
|
|
64
|
+
#n = /* @__PURE__ */ new Map();
|
|
65
|
+
/** True while any displayed value is still chasing its target. */
|
|
66
|
+
#u = !1;
|
|
67
|
+
/** Hover lift per grid position (0..1), eased toward the hovered index. */
|
|
68
|
+
#l = [];
|
|
69
|
+
/** Grid position of the hovered cell (-1 = none). */
|
|
70
|
+
#a = -1;
|
|
71
|
+
/**
|
|
72
|
+
* Entrance-wave clock. `0` = armed but not started (first render stamps
|
|
73
|
+
* it); the wave replays on initial seed and grid-shape changes only.
|
|
74
|
+
*/
|
|
75
|
+
#m = 0;
|
|
76
|
+
#r = !1;
|
|
77
|
+
#p = 0;
|
|
78
|
+
/**
|
|
79
|
+
* Cached left/bottom label gutters in bitmap pixels, keyed by the measured
|
|
80
|
+
* font. `hitTest` runs between renders and reuses the last measurement —
|
|
81
|
+
* same trade-off as pie's label reserve.
|
|
82
|
+
*/
|
|
83
|
+
#o = null;
|
|
84
|
+
/** Subscribers notified on setData (no TimeSeriesStore to piggy-back on). */
|
|
85
|
+
#f = [];
|
|
86
|
+
/**
|
|
87
|
+
* Active theme snapshot — seeded from the create env and refreshed by
|
|
88
|
+
* {@link applyTheme}. Lets between-render queries (`getLayerColors` for
|
|
89
|
+
* legends) resolve the same ramp the canvas paints with.
|
|
90
|
+
*/
|
|
91
|
+
#g;
|
|
92
|
+
/** Data extent cached by {@link #rebuildGrid} — see {@link #domain}. */
|
|
93
|
+
#b = { min: 0, max: 1 };
|
|
94
|
+
constructor(s, t) {
|
|
95
|
+
this.#t = P(lt, s ?? {}), this.#r = this.#t.entryMs === 0, this.#g = t ?? null;
|
|
96
|
+
}
|
|
97
|
+
// --- Data ingest -----------------------------------------------------------
|
|
98
|
+
setData(s) {
|
|
99
|
+
const t = this.#x(), e = this.#s.length > 0;
|
|
100
|
+
if (this.#c = s ?? [], this.#M(), e && this.#x() === t) {
|
|
101
|
+
for (const o of this.#s)
|
|
102
|
+
this.#n.has(o.key) || this.#n.set(o.key, o.value);
|
|
103
|
+
this.#u = this.#t.updateMs > 0;
|
|
104
|
+
} else
|
|
105
|
+
this.#n = new Map(this.#s.map((o) => [o.key, o.value])), this.#u = !1, this.#m = 0, this.#r = this.#t.entryMs === 0;
|
|
106
|
+
this.#o = null;
|
|
107
|
+
for (const o of this.#f)
|
|
108
|
+
o();
|
|
109
|
+
}
|
|
110
|
+
getData() {
|
|
111
|
+
return this.#c;
|
|
112
|
+
}
|
|
113
|
+
onDataChanged(s) {
|
|
114
|
+
return this.#f.push(s), () => {
|
|
115
|
+
const t = this.#f.indexOf(s);
|
|
116
|
+
t >= 0 && this.#f.splice(t, 1);
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
updateOptions(s) {
|
|
120
|
+
const t = this.#t.columns, e = this.#t.rows;
|
|
121
|
+
this.#t = P(this.#t, s ?? {}), (this.#t.columns !== t || this.#t.rows !== e) && this.#M(), this.#t.entryMs === 0 && (this.#r = !0), this.#t.updateMs === 0 && (this.#u = !1), this.#o = null;
|
|
122
|
+
}
|
|
123
|
+
// --- Layer model (single-layer no-ops, mirroring pie) ----------------------
|
|
124
|
+
getLayerCount() {
|
|
125
|
+
return 1;
|
|
126
|
+
}
|
|
127
|
+
setLayerVisible(s, t) {
|
|
128
|
+
}
|
|
129
|
+
isLayerVisible(s) {
|
|
130
|
+
return !0;
|
|
131
|
+
}
|
|
132
|
+
getLayerColors() {
|
|
133
|
+
if (this.#g) {
|
|
134
|
+
const s = this.#y(this.#g);
|
|
135
|
+
return [s[s.length - 1]];
|
|
136
|
+
}
|
|
137
|
+
return [this.#t.colors?.[this.#t.colors.length - 1] ?? "#888"];
|
|
138
|
+
}
|
|
139
|
+
applyTheme(s, t) {
|
|
140
|
+
this.#g = s, s.typography.fontFamily !== t.typography.fontFamily && (this.#o = null);
|
|
141
|
+
}
|
|
142
|
+
getTotalLength() {
|
|
143
|
+
return this.#c.length;
|
|
144
|
+
}
|
|
145
|
+
// --- Hover ------------------------------------------------------------------
|
|
146
|
+
hitTest(s, t, e, o, h) {
|
|
147
|
+
const i = O({
|
|
148
|
+
bitmapWidth: e,
|
|
149
|
+
bitmapHeight: o,
|
|
150
|
+
padTop: h?.top ?? 0,
|
|
151
|
+
padBottom: h?.bottom ?? 0,
|
|
152
|
+
gutterLeft: this.#o?.left ?? 0,
|
|
153
|
+
gutterBottom: this.#o?.bottom ?? 0,
|
|
154
|
+
cols: this.#e.length,
|
|
155
|
+
rows: this.#i.length
|
|
156
|
+
});
|
|
157
|
+
if (!i) return -1;
|
|
158
|
+
const n = Math.floor((s - i.originX) / i.slotW), l = Math.floor((t - i.originY) / i.slotH);
|
|
159
|
+
if (n < 0 || n >= this.#e.length || l < 0 || l >= this.#i.length) return -1;
|
|
160
|
+
const a = l * this.#e.length + n;
|
|
161
|
+
return this.#h[a] >= 0 ? a : -1;
|
|
162
|
+
}
|
|
163
|
+
setHoverIndex(s) {
|
|
164
|
+
return this.#a === s ? !1 : (this.#a = s, !0);
|
|
165
|
+
}
|
|
166
|
+
getHoverIndex() {
|
|
167
|
+
return this.#a;
|
|
168
|
+
}
|
|
169
|
+
getHoverInfo(s) {
|
|
170
|
+
const t = this.#v(this.#a);
|
|
171
|
+
if (!t) return null;
|
|
172
|
+
const { min: e, max: o } = this.#d(), h = o - e, i = h > 0 ? v((t.value - e) / h, 0, 1) : 0;
|
|
173
|
+
return {
|
|
174
|
+
label: `${t.y} · ${t.x}`,
|
|
175
|
+
value: t.value,
|
|
176
|
+
percent: i * 100,
|
|
177
|
+
color: t.color ?? Y(this.#y(s), i)
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
// --- Animation --------------------------------------------------------------
|
|
181
|
+
get needsAnimation() {
|
|
182
|
+
if (this.#s.length === 0) return !1;
|
|
183
|
+
if (!this.#r || this.#u) return !0;
|
|
184
|
+
if (this.#t.updateMs > 0)
|
|
185
|
+
for (let s = 0; s < this.#l.length; s++) {
|
|
186
|
+
const t = s === this.#a ? 1 : 0;
|
|
187
|
+
if (Math.abs(this.#l[s] - t) > 0.01) return !0;
|
|
188
|
+
}
|
|
189
|
+
return !1;
|
|
190
|
+
}
|
|
191
|
+
dispose() {
|
|
192
|
+
this.#f = [], this.#c = [], this.#s = [], this.#h = [], this.#e = [], this.#i = [], this.#n.clear(), this.#l = [], this.#p = 0;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* @internal Test-only accessor for the animated per-cell values (keyed by
|
|
196
|
+
* `x\u0000y`). The backing map is ECMAScript-private, so the animation
|
|
197
|
+
* regression tests read convergence through this. Do not call from
|
|
198
|
+
* production code.
|
|
199
|
+
*/
|
|
200
|
+
_inspectDisplayedValues() {
|
|
201
|
+
return this.#n;
|
|
202
|
+
}
|
|
203
|
+
// --- Grid resolution ---------------------------------------------------------
|
|
204
|
+
/** Identity of the grid shape — same key means cells map to the same slots. */
|
|
205
|
+
#x() {
|
|
206
|
+
return `${this.#e.join("\0")}${this.#i.join("\0")}`;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Resolve column/row orders (explicit option order, else first appearance)
|
|
210
|
+
* and place every datum. On duplicate (x, y) coordinates the last datum
|
|
211
|
+
* wins — matching "latest sample replaces" streaming intuition.
|
|
212
|
+
*/
|
|
213
|
+
#M() {
|
|
214
|
+
const s = this.#x(), t = this.#t.columns ? [...this.#t.columns] : [], e = this.#t.rows ? [...this.#t.rows] : [], o = new Map(t.map((i, n) => [i, n])), h = new Map(e.map((i, n) => [i, n]));
|
|
215
|
+
for (const i of this.#c)
|
|
216
|
+
!this.#t.columns && !o.has(i.x) && (o.set(i.x, t.length), t.push(i.x)), !this.#t.rows && !h.has(i.y) && (h.set(i.y, e.length), e.push(i.y));
|
|
217
|
+
this.#e = t, this.#i = e, this.#s = [], this.#h = new Array(t.length * e.length).fill(-1);
|
|
218
|
+
for (const i of this.#c) {
|
|
219
|
+
const n = o.get(i.x), l = h.get(i.y);
|
|
220
|
+
if (n === void 0 || l === void 0) continue;
|
|
221
|
+
const a = l * t.length + n, u = this.#h[a], c = {
|
|
222
|
+
x: i.x,
|
|
223
|
+
y: i.y,
|
|
224
|
+
value: i.value,
|
|
225
|
+
color: i.color,
|
|
226
|
+
col: n,
|
|
227
|
+
row: l,
|
|
228
|
+
key: at(i.x, i.y)
|
|
229
|
+
};
|
|
230
|
+
u >= 0 ? this.#s[u] = c : (this.#h[a] = this.#s.length, this.#s.push(c));
|
|
231
|
+
}
|
|
232
|
+
this.#x() !== s && (this.#l = new Array(this.#h.length).fill(0), this.#a = -1), this.#b = this.#w();
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Scan the placed cells for the finite-value extent. Called once per
|
|
236
|
+
* {@link #rebuildGrid} — render / tooltip / tick paths read the cached
|
|
237
|
+
* {@link #dataExtent} instead of rescanning every frame.
|
|
238
|
+
*/
|
|
239
|
+
#w() {
|
|
240
|
+
let s = 1 / 0, t = -1 / 0;
|
|
241
|
+
for (const e of this.#s)
|
|
242
|
+
Number.isFinite(e.value) && (e.value < s && (s = e.value), e.value > t && (t = e.value));
|
|
243
|
+
return s === 1 / 0 && (s = 0, t = 1), { min: s, max: t };
|
|
244
|
+
}
|
|
245
|
+
#v(s) {
|
|
246
|
+
if (s < 0 || s >= this.#h.length) return null;
|
|
247
|
+
const t = this.#h[s];
|
|
248
|
+
return t >= 0 ? this.#s[t] : null;
|
|
249
|
+
}
|
|
250
|
+
/** Value domain: explicit option bounds win, else the cached data extent. */
|
|
251
|
+
#d() {
|
|
252
|
+
const { min: s, max: t } = this.#b;
|
|
253
|
+
return {
|
|
254
|
+
min: this.#t.min ?? s,
|
|
255
|
+
max: this.#t.max ?? Math.max(t, this.#t.min ?? s)
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
/** Ramp stops: options override → theme token → accent-over-surface derivation. */
|
|
259
|
+
#y(s) {
|
|
260
|
+
return this.#t.colors && this.#t.colors.length > 0 ? this.#t.colors : s.heatmap && s.heatmap.colors.length > 0 ? s.heatmap.colors : st(s.background, s.line.color);
|
|
261
|
+
}
|
|
262
|
+
// --- Render -------------------------------------------------------------------
|
|
263
|
+
render(s) {
|
|
264
|
+
if (this.#s.length === 0) return;
|
|
265
|
+
const { scope: t, theme: e, padding: o } = s, { context: h, bitmapSize: i, horizontalPixelRatio: n, verticalPixelRatio: l } = t, a = performance.now(), u = this.#p ? Math.min(0.05, (a - this.#p) / 1e3) : 0;
|
|
266
|
+
this.#p = a, this.#m === 0 && (this.#m = a), this.#H(u), this.#L(u);
|
|
267
|
+
const { min: c, max: f } = this.#d(), M = f - c, X = this.#y(e), H = Z(e), L = `${H * n}px ${e.typography.fontFamily}`, T = this.#t.axisLabels ? this.#T({ context: h, font: L, fontSize: H, hpr: n, vpr: l }) : { left: 0, bottom: 0 }, m = O({
|
|
268
|
+
bitmapWidth: i.width,
|
|
269
|
+
bitmapHeight: i.height,
|
|
270
|
+
padTop: o.top * l,
|
|
271
|
+
padBottom: o.bottom * l,
|
|
272
|
+
gutterLeft: T.left,
|
|
273
|
+
gutterBottom: T.bottom,
|
|
274
|
+
cols: this.#e.length,
|
|
275
|
+
rows: this.#i.length
|
|
276
|
+
});
|
|
277
|
+
if (!m) {
|
|
278
|
+
this.#r = !0;
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
const N = this.#t.gap * n, _ = this.#t.gap * l, I = this.#t.cornerRadius * n, A = h.globalAlpha, { entryMs: x } = this.#t, U = Math.max(1, this.#e.length + this.#i.length - 2);
|
|
282
|
+
let $ = !0;
|
|
283
|
+
const d = this.#t.cellLabels, V = d ? `${d.fontSize * n}px ${e.typography.fontFamily}` : "";
|
|
284
|
+
for (const p of this.#s) {
|
|
285
|
+
let y = 1;
|
|
286
|
+
if (x > 0 && !this.#r) {
|
|
287
|
+
const K = (p.col + p.row) / U * x, D = v((a - this.#m - K) / x, 0, 1);
|
|
288
|
+
if (D < 1 && ($ = !1), y = J(D), y <= 0) continue;
|
|
289
|
+
}
|
|
290
|
+
const w = this.#n.get(p.key) ?? p.value, j = Number.isFinite(w) && M > 0 ? v((w - c) / M, 0, 1) : 0;
|
|
291
|
+
let b = p.color ?? Y(X, j);
|
|
292
|
+
const G = p.row * this.#e.length + p.col, g = this.#l[G] ?? 0;
|
|
293
|
+
g > 0.01 && (b = et(b, e.tooltip.textColor, 0.1 * g), h.shadowColor = "rgba(0, 0, 0, 0.3)", h.shadowBlur = 12 * n * g, h.shadowOffsetY = 3 * l * g);
|
|
294
|
+
const S = Math.max(0, m.slotW - N), W = Math.max(0, m.slotH - _), F = (0.82 + 0.18 * y) * (1 + 0.04 * g), R = S * F, B = W * F, C = m.originX + p.col * m.slotW + m.slotW / 2, k = m.originY + p.row * m.slotH + m.slotH / 2;
|
|
295
|
+
h.globalAlpha = A * y, h.fillStyle = b, nt(h, {
|
|
296
|
+
x: C - R / 2,
|
|
297
|
+
y: k - B / 2,
|
|
298
|
+
width: R,
|
|
299
|
+
height: B,
|
|
300
|
+
radius: I,
|
|
301
|
+
corners: { tl: !0, tr: !0, br: !0, bl: !0 }
|
|
302
|
+
}), g > 0.01 && (h.shadowColor = "transparent", h.shadowBlur = 0, h.shadowOffsetY = 0), d && this.#A({
|
|
303
|
+
context: h,
|
|
304
|
+
cell: { fill: b, displayed: w, cx: C, cy: k, innerW: S, innerH: W, span: M },
|
|
305
|
+
labels: d,
|
|
306
|
+
font: V,
|
|
307
|
+
hpr: n
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
x > 0 && !this.#r && $ && (this.#r = !0), h.globalAlpha = A, this.#t.axisLabels && this.#$({ context: h, theme: e, geo: m, font: L, hpr: n, vpr: l });
|
|
311
|
+
}
|
|
312
|
+
/** Chase displayed values toward their targets; clears the settling flag on convergence. */
|
|
313
|
+
#H(s) {
|
|
314
|
+
if (!this.#u) return;
|
|
315
|
+
const { min: t, max: e } = this.#d(), o = Math.max(1e-9, (e - t) * 1e-3), h = 3e3 / this.#t.updateMs;
|
|
316
|
+
let i = !0;
|
|
317
|
+
for (const n of this.#s) {
|
|
318
|
+
const l = this.#n.get(n.key) ?? n.value, a = z(l, n.value, h, s);
|
|
319
|
+
Math.abs(a - n.value) > o ? (i = !1, this.#n.set(n.key, a)) : this.#n.set(n.key, n.value);
|
|
320
|
+
}
|
|
321
|
+
i && (this.#u = !1);
|
|
322
|
+
}
|
|
323
|
+
/** Ease per-cell hover lift toward the hovered position (or snap when updates are off). */
|
|
324
|
+
#L(s) {
|
|
325
|
+
const t = this.#t.updateMs > 0;
|
|
326
|
+
for (let e = 0; e < this.#l.length; e++) {
|
|
327
|
+
const o = e === this.#a ? 1 : 0;
|
|
328
|
+
this.#l[e] = t ? z(this.#l[e], o, 12, s) : o;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Measure the row-label gutter (widest row key) and column-label gutter.
|
|
333
|
+
* Cached by font string until data / options / typography change.
|
|
334
|
+
*/
|
|
335
|
+
#T(s) {
|
|
336
|
+
const { context: t, font: e, fontSize: o, hpr: h, vpr: i } = s;
|
|
337
|
+
if (this.#o && this.#o.font === e)
|
|
338
|
+
return { left: this.#o.left, bottom: this.#o.bottom };
|
|
339
|
+
const n = t.font;
|
|
340
|
+
t.font = e;
|
|
341
|
+
let l = 0;
|
|
342
|
+
for (const c of this.#i) {
|
|
343
|
+
const f = t.measureText(c).width;
|
|
344
|
+
f > l && (l = f);
|
|
345
|
+
}
|
|
346
|
+
t.font = n;
|
|
347
|
+
const a = l > 0 ? l + 8 * h : 0, u = o * i + 8 * i;
|
|
348
|
+
return this.#o = { font: e, left: a, bottom: u }, { left: a, bottom: u };
|
|
349
|
+
}
|
|
350
|
+
/** Value text inside a cell — skipped when the rendered text doesn't fit. */
|
|
351
|
+
#A(s) {
|
|
352
|
+
const { context: t, cell: e, labels: o, font: h, hpr: i } = s, n = o.fontSize * 1.4 * i;
|
|
353
|
+
if (e.innerH < n) return;
|
|
354
|
+
t.font = h;
|
|
355
|
+
const l = o.format ? o.format(e.displayed) : ht(e.displayed, e.span);
|
|
356
|
+
t.measureText(l).width > e.innerW - 6 * i || (t.fillStyle = it(e.fill) ? "#1f2328" : "#ffffff", t.textAlign = "center", t.textBaseline = "middle", t.fillText(l, e.cx, e.cy));
|
|
357
|
+
}
|
|
358
|
+
/** Row labels down the left edge; column labels along the bottom, density-thinned. */
|
|
359
|
+
#$(s) {
|
|
360
|
+
const { context: t, theme: e, geo: o, font: h, hpr: i, vpr: n } = s;
|
|
361
|
+
t.font = h, t.fillStyle = tt(e), t.textAlign = "right", t.textBaseline = "middle";
|
|
362
|
+
for (let c = 0; c < this.#i.length; c++) {
|
|
363
|
+
const f = o.originY + c * o.slotH + o.slotH / 2;
|
|
364
|
+
t.fillText(this.#i[c], o.originX - 6 * i, f);
|
|
365
|
+
}
|
|
366
|
+
let l = 0;
|
|
367
|
+
for (const c of this.#e) {
|
|
368
|
+
const f = t.measureText(c).width;
|
|
369
|
+
f > l && (l = f);
|
|
370
|
+
}
|
|
371
|
+
const a = Math.max(1, Math.ceil((l + 6 * i) / o.slotW));
|
|
372
|
+
t.textAlign = "center", t.textBaseline = "top";
|
|
373
|
+
const u = o.originY + this.#i.length * o.slotH + 6 * n;
|
|
374
|
+
for (let c = 0; c < this.#e.length; c += a) {
|
|
375
|
+
const f = o.originX + c * o.slotW + o.slotW / 2;
|
|
376
|
+
t.fillText(this.#e[c], f, u);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
const yt = {
|
|
381
|
+
type: "heatmap",
|
|
382
|
+
create: (r, s) => new ct({ ...s }, r.theme)
|
|
383
|
+
};
|
|
384
|
+
export {
|
|
385
|
+
ct as HeatmapRenderer,
|
|
386
|
+
yt as HeatmapSeriesDef
|
|
387
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { easeInOutCubic as f, easeOutBack as d } from "../animation/easing.js";
|
|
2
|
+
function l(e) {
|
|
3
|
+
return e <= 0 ? 0 : e >= 1 ? 1 : e;
|
|
4
|
+
}
|
|
5
|
+
function g(e) {
|
|
6
|
+
const o = f(e.progress), t = e.range.from + o * (e.range.to - e.range.from);
|
|
7
|
+
return { x: e.timeToX(t), time: t };
|
|
8
|
+
}
|
|
9
|
+
function X(e = {}) {
|
|
10
|
+
const o = e.head ?? !0;
|
|
11
|
+
return (t) => {
|
|
12
|
+
const r = g(t);
|
|
13
|
+
return { clip: { toX: r.x }, heads: o ? [r] : void 0 };
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function y(e = {}) {
|
|
17
|
+
const o = Math.min(l(e.stagger ?? 0.35), 0.9);
|
|
18
|
+
return (t) => ({
|
|
19
|
+
value: ({ layerIndex: r, value: u, position: a }) => {
|
|
20
|
+
const s = t.layerMean(r);
|
|
21
|
+
if (s === null || !Number.isFinite(u)) return u;
|
|
22
|
+
const n = l((t.progress - o * a) / (1 - o));
|
|
23
|
+
return s + (u - s) * d(n);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function T() {
|
|
28
|
+
const e = X();
|
|
29
|
+
return (o) => {
|
|
30
|
+
if (o.stacking !== "off") return e(o);
|
|
31
|
+
const t = o.primaryPath();
|
|
32
|
+
if (t === null || t.length < 2) return e(o);
|
|
33
|
+
let r = 0;
|
|
34
|
+
const u = [];
|
|
35
|
+
for (let n = 1; n < t.length; n++) {
|
|
36
|
+
const i = Math.hypot(t[n].x - t[n - 1].x, t[n].y - t[n - 1].y);
|
|
37
|
+
u.push(i), r += i;
|
|
38
|
+
}
|
|
39
|
+
if (r <= 0) return e(o);
|
|
40
|
+
let a = o.progress * r;
|
|
41
|
+
for (let n = 1; n < t.length; n++) {
|
|
42
|
+
const i = u[n - 1];
|
|
43
|
+
if (a > i) {
|
|
44
|
+
a -= i;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
const h = i > 0 ? a / i : 1, c = t[n - 1], p = t[n], m = c.x + (p.x - c.x) * h, x = c.time + (p.time - c.time) * h;
|
|
48
|
+
return { clip: { toX: m }, heads: [{ x: m, time: x }] };
|
|
49
|
+
}
|
|
50
|
+
const s = t[t.length - 1];
|
|
51
|
+
return { clip: { toX: s.x }, heads: [{ x: s.x, time: s.time }] };
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function w(e = {}) {
|
|
55
|
+
const o = l(e.ghostAlpha ?? 0.18);
|
|
56
|
+
return (t) => {
|
|
57
|
+
const r = g(t);
|
|
58
|
+
return { clip: { toX: r.x }, heads: [r], ghostAlpha: o };
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function b() {
|
|
62
|
+
return (e) => ({
|
|
63
|
+
clip: { toX: f(e.progress) * e.width }
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
function k() {
|
|
67
|
+
return (e) => {
|
|
68
|
+
const o = f(e.progress), t = e.width / 2, r = t * o;
|
|
69
|
+
return {
|
|
70
|
+
clip: { fromX: t - r, toX: t + r },
|
|
71
|
+
heads: [
|
|
72
|
+
{ x: t - r, time: e.xToTime(t - r) },
|
|
73
|
+
{ x: t + r, time: e.xToTime(t + r) }
|
|
74
|
+
]
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
export {
|
|
79
|
+
b as backfillSweepIntro,
|
|
80
|
+
k as centerOutIntro,
|
|
81
|
+
T as plotterIntro,
|
|
82
|
+
X as sweepIntro,
|
|
83
|
+
w as traceIntro,
|
|
84
|
+
y as unfoldIntro
|
|
85
|
+
};
|