@wick-charts/vue 0.6.0 → 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/README.md +27 -3
- package/dist/ChartContainer.vue.js +99 -69
- 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/options.js +27 -16
- package/dist/core/dist/chart.js +673 -442
- package/dist/core/dist/components/crosshair.js +8 -6
- package/dist/core/dist/components/grid.js +21 -17
- package/dist/core/dist/interactions/handler.js +36 -20
- 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 +123 -1
- package/dist/ui/YLabel.vue.js +19 -18
- package/package.json +2 -2
- package/src/ChartContainer.vue +116 -1
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
function
|
|
2
|
-
const { context: t, bitmapSize:
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
t.
|
|
1
|
+
function u(m) {
|
|
2
|
+
const { scope: x, bitmapX: o, bitmapY: i, theme: M, pane: a } = m, { context: t, bitmapSize: h, horizontalPixelRatio: e, verticalPixelRatio: p } = x, n = o <= a.width, r = i <= a.height;
|
|
3
|
+
if (!n && !r) return;
|
|
4
|
+
const s = Math.max(1, Math.round(p)), c = Math.max(1, Math.round(e));
|
|
5
|
+
t.strokeStyle = M.crosshair.color, t.lineWidth = Math.max(s, c), t.setLineDash([4 * e, 4 * e]);
|
|
6
|
+
const l = Math.round(o) + (c % 2 === 1 ? 0.5 : 0), d = Math.round(i) + (s % 2 === 1 ? 0.5 : 0);
|
|
7
|
+
t.beginPath(), n && (t.moveTo(l, 0), t.lineTo(l, h.height)), r && (t.moveTo(0, d), t.lineTo(h.width, d)), t.stroke(), t.setLineDash([]);
|
|
6
8
|
}
|
|
7
9
|
export {
|
|
8
|
-
|
|
10
|
+
u as renderCrosshair
|
|
9
11
|
};
|
|
@@ -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
|
};
|
|
@@ -6,8 +6,8 @@ function i(o, e) {
|
|
|
6
6
|
const t = o.clientX - e.clientX, s = o.clientY - e.clientY;
|
|
7
7
|
return Math.hypot(t, s);
|
|
8
8
|
}
|
|
9
|
-
const d = 5,
|
|
10
|
-
class
|
|
9
|
+
const d = 5, m = 500;
|
|
10
|
+
class T extends a {
|
|
11
11
|
zoom;
|
|
12
12
|
pan;
|
|
13
13
|
canvas;
|
|
@@ -15,13 +15,27 @@ class M extends a {
|
|
|
15
15
|
yScale;
|
|
16
16
|
target;
|
|
17
17
|
constructor(e, t, s, n) {
|
|
18
|
-
super(), this.canvas = e, this.target = t, this.timeScale = s, this.yScale = n, this.zoom = new r(t, s), this.pan = new u(t, s, e), e.style.cursor = "crosshair",
|
|
18
|
+
super(), this.canvas = e, this.target = t, this.timeScale = s, this.yScale = n, this.zoom = new r(t, s), this.pan = new u(t, s, e), e.style.cursor = "crosshair", this.syncPanZoomMode(), e.addEventListener("wheel", this.onWheel, { passive: !1 }), e.addEventListener("mousedown", this.onMouseDown), e.addEventListener("mousemove", this.onMouseMove), e.addEventListener("mouseup", this.onMouseUp), e.addEventListener("mouseleave", this.onMouseLeave), e.addEventListener("click", this.onClick), e.addEventListener("dblclick", this.onDblClick), e.addEventListener("touchstart", this.onTouchStart, { passive: !1 }), e.addEventListener("touchmove", this.onTouchMove, { passive: !1 }), e.addEventListener("touchend", this.onTouchEnd), e.addEventListener("touchcancel", this.onTouchCancel);
|
|
19
|
+
}
|
|
20
|
+
/** Live read of the target's capability — series can be added or removed at any time. */
|
|
21
|
+
panZoomEnabled() {
|
|
22
|
+
return this.target.canPanZoom?.() ?? !0;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Reflect the target's pan/zoom capability into the canvas CSS. While
|
|
26
|
+
* pan/zoomable, `touch-action: none` hands every touch gesture to the
|
|
27
|
+
* chart; on a spatial-only target native scrolling stays enabled so the
|
|
28
|
+
* chart doesn't trap the page. The host re-invokes this whenever the
|
|
29
|
+
* series mix changes.
|
|
30
|
+
*/
|
|
31
|
+
syncPanZoomMode() {
|
|
32
|
+
this.canvas.style.touchAction = this.panZoomEnabled() ? "none" : "auto";
|
|
19
33
|
}
|
|
20
34
|
onWheel = (e) => {
|
|
21
|
-
this.zoom.handleWheel(e);
|
|
35
|
+
this.panZoomEnabled() && this.zoom.handleWheel(e);
|
|
22
36
|
};
|
|
23
37
|
onMouseDown = (e) => {
|
|
24
|
-
this.zoom.cancelPendingRebound(), this.pan.handleMouseDown(e), this.downClient = { x: e.clientX, y: e.clientY };
|
|
38
|
+
this.zoom.cancelPendingRebound(), this.panZoomEnabled() && this.pan.handleMouseDown(e), this.downClient = { x: e.clientX, y: e.clientY };
|
|
25
39
|
};
|
|
26
40
|
onMouseMove = (e) => {
|
|
27
41
|
this.pan.isDragging() && this.pan.handleMouseMove(e), this.emitCrosshair(e.offsetX, e.offsetY);
|
|
@@ -54,36 +68,38 @@ class M extends a {
|
|
|
54
68
|
// synthesize a phantom 'click'.
|
|
55
69
|
gestureHadTwoFingers = !1;
|
|
56
70
|
onTouchStart = (e) => {
|
|
57
|
-
e.preventDefault(), this.zoom.cancelPendingRebound(), this.touchCount = e.touches.length, e.touches.length === 1 ? (this.pan.handleMouseDown({
|
|
71
|
+
this.panZoomEnabled() && (e.preventDefault(), this.zoom.cancelPendingRebound(), this.touchCount = e.touches.length, e.touches.length === 1 ? (this.pan.handleMouseDown({
|
|
58
72
|
button: 0,
|
|
59
73
|
clientX: e.touches[0].clientX
|
|
60
|
-
}), this.downClient = { x: e.touches[0].clientX, y: e.touches[0].clientY }, this.touchDownTime = Date.now(), this.gestureHadTwoFingers = !1) : e.touches.length === 2 && (this.lastTouchDist = i(e.touches[0], e.touches[1]), this.gestureHadTwoFingers = !0);
|
|
74
|
+
}), this.downClient = { x: e.touches[0].clientX, y: e.touches[0].clientY }, this.touchDownTime = Date.now(), this.gestureHadTwoFingers = !1) : e.touches.length === 2 && (this.lastTouchDist = i(e.touches[0], e.touches[1]), this.gestureHadTwoFingers = !0));
|
|
61
75
|
};
|
|
62
76
|
onTouchMove = (e) => {
|
|
63
|
-
if (
|
|
64
|
-
this.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
77
|
+
if (this.panZoomEnabled()) {
|
|
78
|
+
if (e.preventDefault(), e.touches.length === 1 && this.touchCount === 1)
|
|
79
|
+
this.pan.handleMouseMove({
|
|
80
|
+
clientX: e.touches[0].clientX
|
|
81
|
+
});
|
|
82
|
+
else if (e.touches.length === 2) {
|
|
83
|
+
const t = i(e.touches[0], e.touches[1]), s = (e.touches[0].clientX + e.touches[1].clientX) / 2, n = this.canvas.getBoundingClientRect();
|
|
84
|
+
if (t > 0 && this.lastTouchDist > 0) {
|
|
85
|
+
const h = l(this.lastTouchDist / t, 0.1, 10), c = this.timeScale.xToTime(s - n.left);
|
|
86
|
+
this.target.zoomAt(c, h, this.timeScale.getMediaWidth());
|
|
87
|
+
}
|
|
88
|
+
this.lastTouchDist = t;
|
|
72
89
|
}
|
|
73
|
-
this.lastTouchDist = t;
|
|
74
90
|
}
|
|
75
91
|
};
|
|
76
92
|
onTouchEnd = (e) => {
|
|
77
93
|
if (e.touches.length === 0) {
|
|
78
94
|
const t = e.changedTouches[0];
|
|
79
|
-
if (this.touchCount === 1 && !this.gestureHadTwoFingers && t && !this.movedBeyondClickTolerance(t.clientX, t.clientY) && Date.now() - this.touchDownTime <=
|
|
95
|
+
if (this.touchCount === 1 && !this.gestureHadTwoFingers && t && !this.movedBeyondClickTolerance(t.clientX, t.clientY) && Date.now() - this.touchDownTime <= m) {
|
|
80
96
|
const s = this.canvas.getBoundingClientRect();
|
|
81
97
|
this.emit("click", this.posFromOffset(t.clientX - s.left, t.clientY - s.top));
|
|
82
98
|
}
|
|
83
99
|
this.pan.handleMouseUp(), this.touchCount = 0, this.lastTouchDist = 0, this.downClient = null, this.gestureHadTwoFingers = !1, this.emit("crosshairMove", null);
|
|
84
100
|
return;
|
|
85
101
|
}
|
|
86
|
-
e.touches.length === 1 && (this.touchCount = 1, this.lastTouchDist = 0, this.pan.handleMouseDown({ button: 0, clientX: e.touches[0].clientX }), this.downClient = { x: e.touches[0].clientX, y: e.touches[0].clientY }, this.touchDownTime = Date.now());
|
|
102
|
+
e.touches.length === 1 && this.touchCount === 2 && (this.touchCount = 1, this.lastTouchDist = 0, this.pan.handleMouseDown({ button: 0, clientX: e.touches[0].clientX }), this.downClient = { x: e.touches[0].clientX, y: e.touches[0].clientY }, this.touchDownTime = Date.now());
|
|
87
103
|
};
|
|
88
104
|
onTouchCancel = () => {
|
|
89
105
|
this.pan.handleMouseUp(), this.touchCount = 0, this.lastTouchDist = 0, this.downClient = null, this.gestureHadTwoFingers = !1, this.emit("crosshairMove", null);
|
|
@@ -104,5 +120,5 @@ class M extends a {
|
|
|
104
120
|
}
|
|
105
121
|
}
|
|
106
122
|
export {
|
|
107
|
-
|
|
123
|
+
T as InteractionHandler
|
|
108
124
|
};
|
|
@@ -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
|
};
|