@wick-charts/vue 0.6.1 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/dist/animation/config.js +66 -64
- package/dist/core/dist/animation/intro-wave.js +19 -21
- package/dist/core/dist/animation/reduced-motion.js +6 -0
- package/dist/core/dist/axis/dom-labels.js +14 -14
- package/dist/core/dist/chart/badge-animator.js +15 -19
- package/dist/core/dist/chart.js +448 -417
- package/dist/core/dist/components/grid.js +21 -17
- package/dist/core/dist/navigator/controller.js +12 -12
- package/dist/core/dist/scales/tick-tracker.js +2 -2
- package/dist/core/dist/scales/x-scale.js +47 -32
- package/dist/core/dist/scales/y-scale.js +39 -21
- package/dist/core/dist/series/bar.js +24 -24
- package/dist/core/dist/series/candlestick.js +20 -20
- package/dist/core/dist/series/heatmap.js +16 -16
- package/dist/core/dist/series/line.js +461 -401
- package/dist/core/dist/utils/pixel-grid.js +10 -0
- package/dist/index.d.ts +33 -1
- package/dist/ui/YLabel.vue.js +19 -18
- package/package.json +2 -2
|
@@ -1,22 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
import { crispLineWidth as h, crispCenterOffset as d } from "../utils/pixel-grid.js";
|
|
2
|
+
function x({ scope: p, timeScale: m, yScale: f, theme: a, yTicks: u, timeTicks: T, alpha: n = 1 }) {
|
|
3
|
+
if (n <= 0.01) return;
|
|
4
|
+
const { context: e, bitmapSize: c, horizontalPixelRatio: t, verticalPixelRatio: l } = p;
|
|
5
|
+
e.save(), e.strokeStyle = a.grid.color, a.grid.style === "dashed" ? e.setLineDash([4 * t, 4 * t]) : a.grid.style === "dotted" && e.setLineDash([1 * t, 3 * t]);
|
|
6
|
+
const g = h(l), v = d(l);
|
|
7
|
+
e.lineWidth = g;
|
|
8
|
+
for (const { value: r, opacity: s } of u.entries) {
|
|
9
|
+
const i = s * n;
|
|
10
|
+
if (i <= 0.01) continue;
|
|
11
|
+
const o = Math.round(f.valueToBitmapY(r)) + v;
|
|
12
|
+
e.globalAlpha = i, e.beginPath(), e.moveTo(0, o), e.lineTo(c.width, o), e.stroke();
|
|
10
13
|
}
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
for (const { value:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
const y = h(t), b = d(t);
|
|
15
|
+
e.lineWidth = y;
|
|
16
|
+
for (const { value: r, opacity: s } of T.entries) {
|
|
17
|
+
const i = s * n;
|
|
18
|
+
if (i <= 0.01) continue;
|
|
19
|
+
const o = Math.round(m.timeToBitmapX(r)) + b;
|
|
20
|
+
e.globalAlpha = i, e.beginPath(), e.moveTo(o, 0), e.lineTo(o, c.height), e.stroke();
|
|
17
21
|
}
|
|
18
|
-
|
|
22
|
+
e.restore();
|
|
19
23
|
}
|
|
20
24
|
export {
|
|
21
|
-
|
|
25
|
+
x as renderGrid
|
|
22
26
|
};
|
|
@@ -2,8 +2,8 @@ import { DEFAULT_LINE_ENTRY as w } from "../animation/config.js";
|
|
|
2
2
|
import { XScale as T } from "../scales/x-scale.js";
|
|
3
3
|
import { YScale as R } from "../scales/y-scale.js";
|
|
4
4
|
import { decimateCandles as k, decimateLinear as C } from "./decimate.js";
|
|
5
|
-
import { hitTest as y, computeSnapCenter as E, computePan as I, computeResize as
|
|
6
|
-
import { computeWindowGeometry as
|
|
5
|
+
import { hitTest as y, computeSnapCenter as E, computePan as I, computeResize as $ } from "./interactions.js";
|
|
6
|
+
import { computeWindowGeometry as M, renderBackground as V, renderMiniCandlestick as L, renderMiniBar as N, renderMiniLine as P } from "./render.js";
|
|
7
7
|
import { Animator as S } from "../animation/animator.js";
|
|
8
8
|
import { easeOutCubic as z } from "../animation/easing.js";
|
|
9
9
|
class G {
|
|
@@ -58,8 +58,8 @@ class G {
|
|
|
58
58
|
#g = null;
|
|
59
59
|
// Bound listeners so we can remove the same references on destroy.
|
|
60
60
|
#I;
|
|
61
|
-
#M;
|
|
62
61
|
#$;
|
|
62
|
+
#M;
|
|
63
63
|
#V;
|
|
64
64
|
#L;
|
|
65
65
|
#b;
|
|
@@ -68,9 +68,9 @@ class G {
|
|
|
68
68
|
this.#s = t.container, this.#e = t.chart, this.#x = t.data, this.#v = t.options ?? {}, window.getComputedStyle(this.#s).position === "static" && (this.#s.style.position = "relative"), this.#t = document.createElement("canvas"), this.#t.style.position = "absolute", this.#t.style.left = "0", this.#t.style.top = "0", this.#t.style.height = "100%", this.#t.style.touchAction = "none", this.#s.appendChild(this.#t);
|
|
69
69
|
const e = this.#t.getContext("2d", { alpha: !0 });
|
|
70
70
|
if (!e) throw new Error("NavigatorController: failed to acquire 2D context");
|
|
71
|
-
this.#z = e, this.#o = document.createElement("div"), this.#o.dataset.chartNavigatorOverlay = "", this.#o.style.cssText = "position:absolute;left:0;top:0;height:100%;pointer-events:none;", this.#d = document.createElement("div"), this.#d.style.cssText = "position:absolute;left:0;top:0;bottom:0;", this.#r = document.createElement("div"), this.#r.style.cssText = "position:absolute;top:0;bottom:0;", this.#l = document.createElement("div"), this.#l.style.cssText = "position:absolute;top:0;bottom:0;box-sizing:border-box;", this.#a = b(), this.#c = b(), this.#o.append(this.#d, this.#r, this.#l, this.#a, this.#c), this.#s.appendChild(this.#o), this.#I = () => this.#B(), this
|
|
71
|
+
this.#z = e, this.#o = document.createElement("div"), this.#o.dataset.chartNavigatorOverlay = "", this.#o.style.cssText = "position:absolute;left:0;top:0;height:100%;pointer-events:none;", this.#d = document.createElement("div"), this.#d.style.cssText = "position:absolute;left:0;top:0;bottom:0;", this.#r = document.createElement("div"), this.#r.style.cssText = "position:absolute;top:0;bottom:0;", this.#l = document.createElement("div"), this.#l.style.cssText = "position:absolute;top:0;bottom:0;box-sizing:border-box;", this.#a = b(), this.#c = b(), this.#o.append(this.#d, this.#r, this.#l, this.#a, this.#c), this.#s.appendChild(this.#o), this.#I = () => this.#B(), this.#$ = () => this.#f(), this.#M = () => this.#B(), this.#e.on("viewportChange", this.#I), this.#e.on("overlayChange", this.#$), this.#e.on("tickFrame", this.#M), this.#V = (s) => this.#U(s), this.#L = (s) => this.#J(s), this.#b = (s) => this.#K(s), this.#N = () => {
|
|
72
72
|
this.#g === null && (this.#t.style.cursor = "default");
|
|
73
|
-
}, this.#t.addEventListener("pointerdown", this.#V), this.#t.addEventListener("pointermove", this.#L), this.#t.addEventListener("pointerup", this.#b), this.#t.addEventListener("pointercancel", this.#b), this.#t.addEventListener("pointerleave", this.#N), this.#E = new ResizeObserver((s) => this.#
|
|
73
|
+
}, this.#t.addEventListener("pointerdown", this.#V), this.#t.addEventListener("pointermove", this.#L), this.#t.addEventListener("pointerup", this.#b), this.#t.addEventListener("pointercancel", this.#b), this.#t.addEventListener("pointerleave", this.#N), this.#E = new ResizeObserver((s) => this.#j(s[0])), this.#E.observe(this.#s), this.#D();
|
|
74
74
|
const i = this.#s.getBoundingClientRect();
|
|
75
75
|
i.width > 0 && i.height > 0 && this.#W(i.width, i.height), this.#f();
|
|
76
76
|
}
|
|
@@ -100,7 +100,7 @@ class G {
|
|
|
100
100
|
t !== void 0 && (this.#s.style.height = `${t}px`);
|
|
101
101
|
}
|
|
102
102
|
destroy() {
|
|
103
|
-
this.#e.off("viewportChange", this.#I), this.#e.off("overlayChange", this
|
|
103
|
+
this.#e.off("viewportChange", this.#I), this.#e.off("overlayChange", this.#$), this.#e.off("tickFrame", this.#M), this.#t.removeEventListener("pointerdown", this.#V), this.#t.removeEventListener("pointermove", this.#L), this.#t.removeEventListener("pointerup", this.#b), this.#t.removeEventListener("pointercancel", this.#b), this.#t.removeEventListener("pointerleave", this.#N), this.#E.disconnect(), this.#p !== null && (cancelAnimationFrame(this.#p), this.#p = null), this.#t.remove(), this.#o.remove();
|
|
104
104
|
}
|
|
105
105
|
/** CSS pixel height of the strip — theme default unless overridden. */
|
|
106
106
|
get height() {
|
|
@@ -112,7 +112,7 @@ class G {
|
|
|
112
112
|
get #i() {
|
|
113
113
|
return Math.max(0, this.#A - this.#e.yAxisWidth);
|
|
114
114
|
}
|
|
115
|
-
#
|
|
115
|
+
#j(t) {
|
|
116
116
|
if (!t) return;
|
|
117
117
|
const e = t.contentBoxSize?.[0], i = e?.inlineSize ?? t.contentRect.width, s = e?.blockSize ?? t.contentRect.height;
|
|
118
118
|
i <= 0 || s <= 0 || (this.#W(i, s), this.#f());
|
|
@@ -169,17 +169,17 @@ class G {
|
|
|
169
169
|
return this.#g !== null ? this.#e.getVisibleRange() : this.#e.getAnimationState().xRange;
|
|
170
170
|
}
|
|
171
171
|
#S() {
|
|
172
|
-
return this.#P(),
|
|
172
|
+
return this.#P(), M(this.#u, this.#Y(), this.#y());
|
|
173
173
|
}
|
|
174
|
-
#
|
|
174
|
+
#H() {
|
|
175
175
|
const t = this.#y(), e = t.to - t.from;
|
|
176
176
|
return e <= 0 || this.#i <= 0 ? 0 : this.#i / e;
|
|
177
177
|
}
|
|
178
|
-
#
|
|
178
|
+
#U(t) {
|
|
179
179
|
if (t.button !== void 0 && t.button !== 0) return;
|
|
180
180
|
const e = this.#w(t), i = this.#y();
|
|
181
181
|
if (i.to <= i.from) return;
|
|
182
|
-
const s = this.#S(), n = y(e, s), h = this.#
|
|
182
|
+
const s = this.#S(), n = y(e, s), h = this.#H();
|
|
183
183
|
if (h === 0) return;
|
|
184
184
|
let l = this.#e.getVisibleRange();
|
|
185
185
|
if (n.snapToCenter) {
|
|
@@ -216,7 +216,7 @@ class G {
|
|
|
216
216
|
});
|
|
217
217
|
else {
|
|
218
218
|
const h = e.gesture === "resize-left" ? "left" : "right";
|
|
219
|
-
n =
|
|
219
|
+
n = $({
|
|
220
220
|
edge: h,
|
|
221
221
|
startVisible: e.startVisible,
|
|
222
222
|
deltaPx: i,
|
|
@@ -8,7 +8,7 @@ function f(s, t) {
|
|
|
8
8
|
if (s[e] !== t[e]) return !1;
|
|
9
9
|
return !0;
|
|
10
10
|
}
|
|
11
|
-
|
|
11
|
+
let m = class {
|
|
12
12
|
#e = [];
|
|
13
13
|
#r = [];
|
|
14
14
|
#t = /* @__PURE__ */ new Map();
|
|
@@ -104,7 +104,7 @@ class m {
|
|
|
104
104
|
reset() {
|
|
105
105
|
this.#e = [], this.#r = [], this.#t.clear(), this.#s = !1;
|
|
106
106
|
}
|
|
107
|
-
}
|
|
107
|
+
};
|
|
108
108
|
export {
|
|
109
109
|
m as AxisTickTracker
|
|
110
110
|
};
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { crispCenterOffset as m } from "../utils/pixel-grid.js";
|
|
2
|
+
import { formatTime as c, niceTimeIntervals as f } from "../utils/time.js";
|
|
3
|
+
import { AxisTickTracker as v } from "./tick-tracker.js";
|
|
4
|
+
const o = 50, d = 80;
|
|
5
|
+
class b {
|
|
5
6
|
/**
|
|
6
7
|
* Shared fade state for time ticks. Grid lines (canvas) and DOM tick labels
|
|
7
8
|
* read opacity from the same tracker, so a tick fading in/out looks
|
|
8
9
|
* identical on both surfaces.
|
|
9
10
|
*/
|
|
10
|
-
tickTracker = new
|
|
11
|
+
tickTracker = new v();
|
|
11
12
|
from = 0;
|
|
12
13
|
to = 0;
|
|
13
14
|
width = 1;
|
|
@@ -37,17 +38,17 @@ class p {
|
|
|
37
38
|
return this.labelCountHintValue;
|
|
38
39
|
}
|
|
39
40
|
get minLabelSpacing() {
|
|
40
|
-
return this.minSpacingValue ??
|
|
41
|
+
return this.minSpacingValue ?? d;
|
|
41
42
|
}
|
|
42
|
-
update(t, e, n,
|
|
43
|
-
if (this.from = t.from, this.to = t.to, this.width = e, this.pixelRatio = n,
|
|
44
|
-
const a = h(
|
|
45
|
-
this.lastBucketKey !== null && this.lastBucketKey !== a && this.resetHysteresis(), this.lastBucketKey = a, this.dataInterval =
|
|
43
|
+
update(t, e, n, l) {
|
|
44
|
+
if (this.from = t.from, this.to = t.to, this.width = e, this.pixelRatio = n, l !== void 0 && l > 0) {
|
|
45
|
+
const a = h(l);
|
|
46
|
+
this.lastBucketKey !== null && this.lastBucketKey !== a && this.resetHysteresis(), this.lastBucketKey = a, this.dataInterval = l;
|
|
46
47
|
}
|
|
47
48
|
this.resolveInterval();
|
|
48
49
|
}
|
|
49
50
|
setLabelCount(t) {
|
|
50
|
-
this.labelCountHintValue =
|
|
51
|
+
this.labelCountHintValue = p(t), this.resetHysteresis(), this.resolveInterval();
|
|
51
52
|
}
|
|
52
53
|
setMinSpacing(t) {
|
|
53
54
|
this.minSpacingValue = I(t), this.resetHysteresis(), this.resolveInterval();
|
|
@@ -88,7 +89,7 @@ class p {
|
|
|
88
89
|
* `formatTime`, applying the installed locale/timezone.
|
|
89
90
|
*/
|
|
90
91
|
formatX(t, e) {
|
|
91
|
-
return this.customFormat ? this.customFormat(t, e) :
|
|
92
|
+
return this.customFormat ? this.customFormat(t, e) : c(t, e, { locale: this.localeValue, timeZone: this.timeZoneValue });
|
|
92
93
|
}
|
|
93
94
|
resetHysteresis() {
|
|
94
95
|
this.lastInterval = null, this.lastWant = null;
|
|
@@ -96,8 +97,22 @@ class p {
|
|
|
96
97
|
timeToX(t) {
|
|
97
98
|
return this.to <= this.from ? 0 : (t - this.from) / (this.to - this.from) * this.width;
|
|
98
99
|
}
|
|
100
|
+
/** X in physical (bitmap) pixels, snapped to the pixel grid — for
|
|
101
|
+
* axis-aligned edges that have to stay crisp. */
|
|
99
102
|
timeToBitmapX(t) {
|
|
100
|
-
return Math.round(this.
|
|
103
|
+
return Math.round(this.timeToBitmapXExact(t));
|
|
104
|
+
}
|
|
105
|
+
/** Same in physical pixels, off the grid — see {@link YScale.valueToBitmapYExact}. */
|
|
106
|
+
timeToBitmapXExact(t) {
|
|
107
|
+
return this.timeToX(t) * this.pixelRatio;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* X position in CSS pixels of the *stroke center* the canvas grid draws for
|
|
111
|
+
* `time`. DOM axis labels position off this instead of raw {@link timeToX}
|
|
112
|
+
* so text and gridline share one pixel grid.
|
|
113
|
+
*/
|
|
114
|
+
timeToSnappedX(t) {
|
|
115
|
+
return (this.timeToBitmapX(t) + m(this.pixelRatio)) / this.pixelRatio;
|
|
101
116
|
}
|
|
102
117
|
xToTime(t) {
|
|
103
118
|
return this.to <= this.from ? this.from : this.from + t / this.width * (this.to - this.from);
|
|
@@ -119,16 +134,16 @@ class p {
|
|
|
119
134
|
niceTickValues(t) {
|
|
120
135
|
if (this.to <= this.from) return { ticks: [], tickInterval: 0 };
|
|
121
136
|
if (this.customTickGenerator) {
|
|
122
|
-
const i = this.customTickGenerator({ from: this.from, to: this.to }, t).slice(0, o),
|
|
123
|
-
return { ticks: i, tickInterval:
|
|
137
|
+
const i = this.customTickGenerator({ from: this.from, to: this.to }, t).slice(0, o), r = i.length >= 2 ? i[1] - i[0] : t;
|
|
138
|
+
return { ticks: i, tickInterval: r };
|
|
124
139
|
}
|
|
125
140
|
if (this.dataInterval !== t) {
|
|
126
141
|
const i = h(t);
|
|
127
142
|
this.lastBucketKey !== null && this.lastBucketKey !== i && this.resetHysteresis(), this.lastBucketKey = i, this.dataInterval = t, this.resolveInterval();
|
|
128
143
|
}
|
|
129
144
|
if (this.resolvedInterval == null) return { ticks: [], tickInterval: 0 };
|
|
130
|
-
const e = this.resolvedInterval, n = Math.ceil(this.from / e) * e,
|
|
131
|
-
for (let i = 0; i <
|
|
145
|
+
const e = this.resolvedInterval, n = Math.ceil(this.from / e) * e, l = Math.max(0, Math.min(o, Math.floor((this.to - n) / e) + 1)), a = [];
|
|
146
|
+
for (let i = 0; i < l; i++) a.push(n + i * e);
|
|
132
147
|
return { ticks: a, tickInterval: e };
|
|
133
148
|
}
|
|
134
149
|
getRange() {
|
|
@@ -147,31 +162,31 @@ class p {
|
|
|
147
162
|
this.resolvedInterval = null;
|
|
148
163
|
return;
|
|
149
164
|
}
|
|
150
|
-
const t =
|
|
151
|
-
let
|
|
165
|
+
const t = f(this.dataInterval), e = this.to - this.from, n = e / this.width * this.minLabelSpacing;
|
|
166
|
+
let l;
|
|
152
167
|
if (this.labelCountHint != null) {
|
|
153
|
-
const
|
|
154
|
-
|
|
168
|
+
const r = Math.max(1, this.labelCountHint);
|
|
169
|
+
l = Math.max(e / r, n);
|
|
155
170
|
} else
|
|
156
|
-
|
|
171
|
+
l = n;
|
|
157
172
|
if (this.lastInterval != null && this.lastWant != null && t.includes(this.lastInterval)) {
|
|
158
|
-
const
|
|
159
|
-
if (
|
|
173
|
+
const r = l >= this.lastWant * 0.8 && l <= this.lastWant * 1.25, u = this.lastInterval >= n;
|
|
174
|
+
if (r && u) {
|
|
160
175
|
this.resolvedInterval = this.lastInterval;
|
|
161
176
|
return;
|
|
162
177
|
}
|
|
163
178
|
}
|
|
164
179
|
let a = t[t.length - 1], i = t.length - 1;
|
|
165
|
-
for (let
|
|
166
|
-
if (t[
|
|
167
|
-
a = t[
|
|
180
|
+
for (let r = 0; r < t.length; r++)
|
|
181
|
+
if (t[r] >= l) {
|
|
182
|
+
a = t[r], i = r;
|
|
168
183
|
break;
|
|
169
184
|
}
|
|
170
185
|
if (this.labelCountHint != null && i > 0 && this.countTicks(a) < this.labelCountHint) {
|
|
171
|
-
const
|
|
172
|
-
|
|
186
|
+
const r = t[i - 1];
|
|
187
|
+
r >= n && (a = r);
|
|
173
188
|
}
|
|
174
|
-
this.resolvedInterval = a, this.lastInterval = a, this.lastWant =
|
|
189
|
+
this.resolvedInterval = a, this.lastInterval = a, this.lastWant = l;
|
|
175
190
|
}
|
|
176
191
|
countTicks(t) {
|
|
177
192
|
if (!(t > 0)) return 0;
|
|
@@ -182,12 +197,12 @@ class p {
|
|
|
182
197
|
function h(s) {
|
|
183
198
|
return s < 6e4 ? 0 : s < 36e5 ? 1 : s < 864e5 ? 2 : 3;
|
|
184
199
|
}
|
|
185
|
-
function
|
|
200
|
+
function p(s) {
|
|
186
201
|
return typeof s == "number" && Number.isFinite(s) && s >= 2 ? Math.floor(s) : null;
|
|
187
202
|
}
|
|
188
203
|
function I(s) {
|
|
189
204
|
return typeof s == "number" && Number.isFinite(s) && s > 0 ? s : null;
|
|
190
205
|
}
|
|
191
206
|
export {
|
|
192
|
-
|
|
207
|
+
b as XScale
|
|
193
208
|
};
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import { formatCompact as o } from "../utils/format.js";
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import { crispCenterOffset as u } from "../utils/pixel-grid.js";
|
|
3
|
+
import { AxisTickTracker as m } from "./tick-tracker.js";
|
|
4
|
+
function c(e) {
|
|
4
5
|
if (!Number.isFinite(e) || e <= 0) return 1;
|
|
5
6
|
const t = 10 ** Math.floor(Math.log10(e)), i = e / t;
|
|
6
7
|
return (i <= 1 ? 1 : i <= 2 ? 2 : i <= 5 ? 5 : 10) * t;
|
|
7
8
|
}
|
|
8
|
-
function
|
|
9
|
+
function v(e) {
|
|
9
10
|
if (!Number.isFinite(e) || e <= 0) return 1;
|
|
10
11
|
const t = 10 ** Math.floor(Math.log10(e)), i = e / t;
|
|
11
12
|
return (i >= 5 ? 5 : i >= 2 ? 2 : 1) * t;
|
|
12
13
|
}
|
|
13
|
-
const
|
|
14
|
-
class
|
|
14
|
+
const l = 50, f = 50;
|
|
15
|
+
class T {
|
|
15
16
|
/** Shared fade state for Y ticks; see {@link TimeScale.tickTracker}. */
|
|
16
|
-
tickTracker = new
|
|
17
|
+
tickTracker = new m();
|
|
17
18
|
min = 0;
|
|
18
19
|
max = 0;
|
|
19
20
|
height = 1;
|
|
@@ -41,7 +42,7 @@ class M {
|
|
|
41
42
|
return this.labelCountHintValue;
|
|
42
43
|
}
|
|
43
44
|
get minLabelSpacing() {
|
|
44
|
-
return this.minSpacingValue ??
|
|
45
|
+
return this.minSpacingValue ?? f;
|
|
45
46
|
}
|
|
46
47
|
/** Recalculate the scale with a new Y range, chart height, and device pixel ratio. */
|
|
47
48
|
update(t, i, a) {
|
|
@@ -49,11 +50,11 @@ class M {
|
|
|
49
50
|
}
|
|
50
51
|
/** Desired label count. Invalid values (NaN, <2, Infinity) clear the hint. */
|
|
51
52
|
setLabelCount(t) {
|
|
52
|
-
this.labelCountHintValue =
|
|
53
|
+
this.labelCountHintValue = x(t), this.resetHysteresis(), this.resolveInterval();
|
|
53
54
|
}
|
|
54
55
|
/** Minimum pixel gap between adjacent labels. */
|
|
55
56
|
setMinSpacing(t) {
|
|
56
|
-
this.minSpacingValue =
|
|
57
|
+
this.minSpacingValue = p(t), this.resetHysteresis(), this.resolveInterval();
|
|
57
58
|
}
|
|
58
59
|
resetHysteresis() {
|
|
59
60
|
this.lastInterval = null, this.lastRawInterval = null;
|
|
@@ -79,9 +80,26 @@ class M {
|
|
|
79
80
|
const i = this.max - this.min;
|
|
80
81
|
return i === 0 ? this.height / 2 : (1 - (t - this.min) / i) * this.height;
|
|
81
82
|
}
|
|
82
|
-
/** Convert a value to a Y position in physical (bitmap) pixels
|
|
83
|
+
/** Convert a value to a Y position in physical (bitmap) pixels, snapped to
|
|
84
|
+
* the pixel grid — for axis-aligned edges that have to stay crisp. */
|
|
83
85
|
valueToBitmapY(t) {
|
|
84
|
-
return Math.round(this.
|
|
86
|
+
return Math.round(this.valueToBitmapYExact(t));
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Same in physical pixels, off the grid. Anti-aliased geometry reads this
|
|
90
|
+
* one: snapping buys a diagonal stroke no sharpness, and during a slow
|
|
91
|
+
* animation it quantises the motion into whole-pixel jumps.
|
|
92
|
+
*/
|
|
93
|
+
valueToBitmapYExact(t) {
|
|
94
|
+
return this.valueToY(t) * this.pixelRatio;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Y position in CSS pixels of the *stroke center* the canvas grid draws for
|
|
98
|
+
* `value`. DOM axis labels position off this instead of raw {@link valueToY}
|
|
99
|
+
* so text and gridline share one pixel grid.
|
|
100
|
+
*/
|
|
101
|
+
valueToSnappedY(t) {
|
|
102
|
+
return (this.valueToBitmapY(t) + u(this.pixelRatio)) / this.pixelRatio;
|
|
85
103
|
}
|
|
86
104
|
/** Convert a Y position in CSS pixels back to a value. */
|
|
87
105
|
yToValue(t) {
|
|
@@ -93,9 +111,9 @@ class M {
|
|
|
93
111
|
* chart height. Pure read — resolution happens in `update()`.
|
|
94
112
|
*/
|
|
95
113
|
niceTickValues() {
|
|
96
|
-
if (this.customTickGenerator) return this.customTickGenerator({ min: this.min, max: this.max }).slice(0,
|
|
114
|
+
if (this.customTickGenerator) return this.customTickGenerator({ min: this.min, max: this.max }).slice(0, l);
|
|
97
115
|
if (this.resolvedInterval == null) return [];
|
|
98
|
-
const t = this.resolvedInterval, i = Math.ceil(this.min / t) * t, a = Math.max(0, Math.min(
|
|
116
|
+
const t = this.resolvedInterval, i = Math.ceil(this.min / t) * t, a = Math.max(0, Math.min(l, Math.floor((this.max - i) / t) + 1)), s = [];
|
|
99
117
|
for (let n = 0; n < a; n++) s.push(i + n * t);
|
|
100
118
|
return s;
|
|
101
119
|
}
|
|
@@ -137,16 +155,16 @@ class M {
|
|
|
137
155
|
const t = this.max - this.min, i = t * this.minLabelSpacing / this.height, a = this.labelCountHint != null ? Math.max(1, this.labelCountHint) : Math.max(1, Math.floor(this.height / this.minLabelSpacing));
|
|
138
156
|
let s = t / a;
|
|
139
157
|
if (s < i && (s = i), this.lastInterval != null && this.lastRawInterval != null) {
|
|
140
|
-
const
|
|
141
|
-
if (
|
|
158
|
+
const r = s >= this.lastRawInterval * 0.8 && s <= this.lastRawInterval * 1.25, h = this.lastInterval >= i;
|
|
159
|
+
if (r && h) {
|
|
142
160
|
this.resolvedInterval = this.lastInterval;
|
|
143
161
|
return;
|
|
144
162
|
}
|
|
145
163
|
}
|
|
146
|
-
let n =
|
|
164
|
+
let n = c(s);
|
|
147
165
|
if (this.labelCountHint != null && this.countTicks(n) < this.labelCountHint) {
|
|
148
|
-
const
|
|
149
|
-
|
|
166
|
+
const r = v(s);
|
|
167
|
+
r >= i && r > 0 && (n = r);
|
|
150
168
|
}
|
|
151
169
|
this.resolvedInterval = n, this.lastInterval = n, this.lastRawInterval = s;
|
|
152
170
|
}
|
|
@@ -156,12 +174,12 @@ class M {
|
|
|
156
174
|
return Math.max(0, Math.floor((this.max - i) / t) + 1);
|
|
157
175
|
}
|
|
158
176
|
}
|
|
159
|
-
function
|
|
177
|
+
function x(e) {
|
|
160
178
|
return typeof e == "number" && Number.isFinite(e) && e >= 2 ? Math.floor(e) : null;
|
|
161
179
|
}
|
|
162
|
-
function
|
|
180
|
+
function p(e) {
|
|
163
181
|
return typeof e == "number" && Number.isFinite(e) && e > 0 ? e : null;
|
|
164
182
|
}
|
|
165
183
|
export {
|
|
166
|
-
|
|
184
|
+
T as YScale
|
|
167
185
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DEFAULT_HISTORY_REVEAL as _, DEFAULT_BAR_INTRO as C, DEFAULT_BAR_SMOOTH as F, DEFAULT_BAR_ENTRY as N } from "../animation/config.js";
|
|
2
2
|
import { growIntro as Q } from "./bar-intro.js";
|
|
3
|
-
import { BaseMultiLayerSeries as
|
|
4
|
-
import { resolveBarPainter as
|
|
3
|
+
import { BaseMultiLayerSeries as Z } from "./base-multi-layer.js";
|
|
4
|
+
import { resolveBarPainter as $ } from "./painters/resolve.js";
|
|
5
5
|
import { fadeIntro as q } from "./wave-intro.js";
|
|
6
6
|
const U = 2, G = {
|
|
7
7
|
// Single fallback resolver; the definition's `create` swaps in `theme.bar.color`.
|
|
@@ -15,7 +15,7 @@ const U = 2, G = {
|
|
|
15
15
|
introMs: C,
|
|
16
16
|
historyRevealMs: _
|
|
17
17
|
};
|
|
18
|
-
function
|
|
18
|
+
function O(y) {
|
|
19
19
|
return {
|
|
20
20
|
...y,
|
|
21
21
|
entryMs: y.entryMs === !1 ? 0 : y.entryMs ?? N,
|
|
@@ -29,7 +29,7 @@ const J = Q(), K = q(), tt = {
|
|
|
29
29
|
bottom: { tl: !1, tr: !1, br: !0, bl: !0 },
|
|
30
30
|
none: { tl: !1, tr: !1, br: !1, bl: !1 }
|
|
31
31
|
};
|
|
32
|
-
class et extends
|
|
32
|
+
class et extends Z {
|
|
33
33
|
kind = "bar";
|
|
34
34
|
/** Resolved drawing context for the in-flight render pass. Set at the top of
|
|
35
35
|
* {@link render}, read by {@link drawAnimatedBar}. */
|
|
@@ -43,10 +43,10 @@ class et extends $ {
|
|
|
43
43
|
/** Per-frame history-reveal directives; `null` when no reveal is in flight. */
|
|
44
44
|
#t = null;
|
|
45
45
|
constructor(e, t) {
|
|
46
|
-
super(e), this.options =
|
|
46
|
+
super(e), this.options = O({ ...G, ...t });
|
|
47
47
|
}
|
|
48
48
|
updateOptions(e) {
|
|
49
|
-
this.options =
|
|
49
|
+
this.options = O({ ...this.options, ...e });
|
|
50
50
|
}
|
|
51
51
|
isEntryEnabled() {
|
|
52
52
|
return (this.options.entryAnimation ?? "fade-grow") !== "none";
|
|
@@ -122,7 +122,7 @@ class et extends $ {
|
|
|
122
122
|
this.tickAnimations(performance.now());
|
|
123
123
|
const { scope: t, timeScale: o, yScale: n } = e;
|
|
124
124
|
if (this.#o = {
|
|
125
|
-
painter:
|
|
125
|
+
painter: $(this.options.barPainter),
|
|
126
126
|
env: {
|
|
127
127
|
ctx: t.context,
|
|
128
128
|
theme: e.theme,
|
|
@@ -188,7 +188,7 @@ class et extends $ {
|
|
|
188
188
|
const p = M.getVisibleData(b.from, b.to).length;
|
|
189
189
|
if (p > l && (l = p), l > 2) break;
|
|
190
190
|
}
|
|
191
|
-
const u = o.barWidthBitmap(a), x = Math.round(30 * v), R = l <= 2 ? Math.min(x, u) : u, r = Math.max(1, Math.round(R * this.options.barWidthRatio) - 2), W = Math.floor(r / 2),
|
|
191
|
+
const u = o.barWidthBitmap(a), x = Math.round(30 * v), R = l <= 2 ? Math.min(x, u) : u, r = Math.max(1, Math.round(R * this.options.barWidthRatio) - 2), W = Math.floor(r / 2), k = this.options.anchor === "right" ? r : W, T = n.getRange().min < 0, f = T ? n.valueToBitmapY(0) : t.bitmapSize.height;
|
|
192
192
|
if (this.stores.length === 1) {
|
|
193
193
|
if (!this.stores[0].isVisible()) return;
|
|
194
194
|
const M = this.stores[0].getVisibleData(b.from, b.to);
|
|
@@ -204,7 +204,7 @@ class et extends $ {
|
|
|
204
204
|
baselineY: f,
|
|
205
205
|
topY: g,
|
|
206
206
|
barHeight: c,
|
|
207
|
-
x: h -
|
|
207
|
+
x: h - k,
|
|
208
208
|
barWidth: r,
|
|
209
209
|
color: s,
|
|
210
210
|
roundEdge: "top",
|
|
@@ -219,7 +219,7 @@ class et extends $ {
|
|
|
219
219
|
baselineY: f,
|
|
220
220
|
topY: f,
|
|
221
221
|
barHeight: c,
|
|
222
|
-
x: h -
|
|
222
|
+
x: h - k,
|
|
223
223
|
barWidth: r,
|
|
224
224
|
color: s,
|
|
225
225
|
roundEdge: "bottom",
|
|
@@ -256,22 +256,22 @@ class et extends $ {
|
|
|
256
256
|
for (let h = 0; h < P.length; h++) {
|
|
257
257
|
const { layer: s, value: g, colorValue: c } = P[h], S = this.resolveLayerColor(s, c), E = this.entranceProgress(s, d);
|
|
258
258
|
if (g >= 0) {
|
|
259
|
-
const
|
|
259
|
+
const H = n.valueToBitmapY(g), V = Math.max(1, f - H);
|
|
260
260
|
this.drawAnimatedBar({
|
|
261
261
|
context: m,
|
|
262
262
|
time: d,
|
|
263
263
|
progress: E,
|
|
264
264
|
baselineY: f,
|
|
265
|
-
topY:
|
|
265
|
+
topY: H,
|
|
266
266
|
barHeight: V,
|
|
267
|
-
x: B -
|
|
267
|
+
x: B - k,
|
|
268
268
|
barWidth: r,
|
|
269
269
|
color: S,
|
|
270
270
|
roundEdge: "top",
|
|
271
271
|
isProjected: i
|
|
272
272
|
});
|
|
273
273
|
} else {
|
|
274
|
-
const
|
|
274
|
+
const H = n.valueToBitmapY(g), V = Math.max(1, H - f);
|
|
275
275
|
this.drawAnimatedBar({
|
|
276
276
|
context: m,
|
|
277
277
|
time: d,
|
|
@@ -279,7 +279,7 @@ class et extends $ {
|
|
|
279
279
|
baselineY: f,
|
|
280
280
|
topY: f,
|
|
281
281
|
barHeight: V,
|
|
282
|
-
x: B -
|
|
282
|
+
x: B - k,
|
|
283
283
|
barWidth: r,
|
|
284
284
|
color: S,
|
|
285
285
|
roundEdge: "bottom",
|
|
@@ -304,7 +304,7 @@ class et extends $ {
|
|
|
304
304
|
const h = i.getVisibleData(l.from, l.to).length;
|
|
305
305
|
if (h > u && (u = h), u > 2) break;
|
|
306
306
|
}
|
|
307
|
-
const x = n.barWidthBitmap(m), R = Math.round(30 * b), r = u <= 2 ? Math.min(R, x) : x, W = Math.max(1, Math.round(r * this.options.barWidthRatio) - 2),
|
|
307
|
+
const x = n.barWidthBitmap(m), R = Math.round(30 * b), r = u <= 2 ? Math.min(R, x) : x, W = Math.max(1, Math.round(r * this.options.barWidthRatio) - 2), k = Math.floor(W / 2), T = this.options.anchor === "right" ? W : k, f = this.stores.map(
|
|
308
308
|
(i, h) => this.getLayerAlpha(h) > 0 ? i.getVisibleData(l.from, l.to) : []
|
|
309
309
|
);
|
|
310
310
|
if (f.every((i) => i.length === 0)) return;
|
|
@@ -332,10 +332,10 @@ class et extends $ {
|
|
|
332
332
|
const c = g[i];
|
|
333
333
|
if (c === 0) continue;
|
|
334
334
|
const S = n.timeToBitmapX(s);
|
|
335
|
-
let E = 0,
|
|
335
|
+
let E = 0, H = 0;
|
|
336
336
|
for (let Y = 0; Y < i; Y++) {
|
|
337
337
|
const w = g[Y];
|
|
338
|
-
w > 0 ? E += w :
|
|
338
|
+
w > 0 ? E += w : H += w;
|
|
339
339
|
}
|
|
340
340
|
const V = this.entranceProgress(i, s), X = this.isProjectedTime(s), z = c > 0 ? P.get(s) === i ? "top" : "none" : B.get(s) === i ? "bottom" : "none";
|
|
341
341
|
if (t) {
|
|
@@ -343,14 +343,14 @@ class et extends $ {
|
|
|
343
343
|
for (const A of g)
|
|
344
344
|
A > 0 ? Y += A : w += A;
|
|
345
345
|
if (c > 0 && Y > 0) {
|
|
346
|
-
const A = E / Y * 100, D = (E + c) / Y * 100, j = a.valueToBitmapY(D), L = a.valueToBitmapY(A),
|
|
346
|
+
const A = E / Y * 100, D = (E + c) / Y * 100, j = a.valueToBitmapY(D), L = a.valueToBitmapY(A), I = Math.max(1, L - j);
|
|
347
347
|
this.drawAnimatedBar({
|
|
348
348
|
context: v,
|
|
349
349
|
time: s,
|
|
350
350
|
progress: V,
|
|
351
351
|
baselineY: L,
|
|
352
352
|
topY: j,
|
|
353
|
-
barHeight:
|
|
353
|
+
barHeight: I,
|
|
354
354
|
x: S - T,
|
|
355
355
|
barWidth: W,
|
|
356
356
|
color: h,
|
|
@@ -359,14 +359,14 @@ class et extends $ {
|
|
|
359
359
|
growBaked: !0
|
|
360
360
|
});
|
|
361
361
|
} else if (c < 0 && w < 0) {
|
|
362
|
-
const A =
|
|
362
|
+
const A = H / w * -100, D = (H + c) / w * -100, j = a.valueToBitmapY(A), L = a.valueToBitmapY(D), I = Math.max(1, L - j);
|
|
363
363
|
this.drawAnimatedBar({
|
|
364
364
|
context: v,
|
|
365
365
|
time: s,
|
|
366
366
|
progress: V,
|
|
367
367
|
baselineY: j,
|
|
368
368
|
topY: j,
|
|
369
|
-
barHeight:
|
|
369
|
+
barHeight: I,
|
|
370
370
|
x: S - T,
|
|
371
371
|
barWidth: W,
|
|
372
372
|
color: h,
|
|
@@ -392,7 +392,7 @@ class et extends $ {
|
|
|
392
392
|
growBaked: !0
|
|
393
393
|
});
|
|
394
394
|
} else {
|
|
395
|
-
const Y = a.valueToBitmapY(
|
|
395
|
+
const Y = a.valueToBitmapY(H), w = a.valueToBitmapY(H + c), A = Math.max(1, w - Y);
|
|
396
396
|
this.drawAnimatedBar({
|
|
397
397
|
context: v,
|
|
398
398
|
time: s,
|
|
@@ -423,7 +423,7 @@ class et extends $ {
|
|
|
423
423
|
const { context: o, baselineY: n, topY: a, barHeight: m, x: v, barWidth: b, color: l, roundEdge: u } = e, x = e.isProjected ? 1 : e.progress, R = this.options.entryAnimation ?? "fade-grow";
|
|
424
424
|
let r = x >= 1 || R === "none" ? { x: v, topY: a, barWidth: b, barHeight: m, alpha: 1 } : this.applyBarTransform(e, x);
|
|
425
425
|
if (!e.isProjected) {
|
|
426
|
-
const
|
|
426
|
+
const k = this.#t?.element, T = k !== void 0 ? this.#r(e.time) : null, f = T !== null ? k : this.#e?.element, M = T ?? (f !== void 0 ? this.#a(e.time) : null);
|
|
427
427
|
if (f !== void 0 && M !== null) {
|
|
428
428
|
const p = f({
|
|
429
429
|
...M,
|