@wick-charts/vue 0.7.0 → 0.8.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 +19 -3
- package/dist/BandSeries.vue.js +40 -30
- package/dist/BarSeries.vue.js +39 -29
- package/dist/CandlestickSeries.vue.js +2 -2
- package/dist/ChartContainer.vue.js +1 -1
- package/dist/LineSeries.vue.js +40 -30
- package/dist/core/dist/chart/badge-animator.js +38 -28
- package/dist/core/dist/chart.js +801 -694
- package/dist/core/dist/data/sync.js +77 -50
- package/dist/core/dist/navigator/controller.js +11 -11
- package/dist/core/dist/scales/time-ticks.js +99 -0
- package/dist/core/dist/scales/x-scale.js +33 -27
- package/dist/core/dist/series/band.js +82 -82
- package/dist/core/dist/series/bar.js +1 -1
- package/dist/core/dist/series/base-multi-layer.js +182 -152
- package/dist/core/dist/series/candlestick.js +96 -96
- package/dist/core/dist/series/line.js +297 -292
- package/dist/core/dist/snapshots.js +39 -35
- package/dist/core/dist/utils/point.js +4 -0
- package/dist/core/dist/utils/time.js +42 -26
- package/dist/index.d.ts +45 -24
- package/dist/ui/InfoBar.vue.js +13 -13
- package/dist/ui/Tooltip.vue.js +11 -11
- package/dist/ui/YLabel.vue.js +47 -47
- package/package.json +9 -3
- package/src/BandSeries.vue +16 -3
- package/src/BarSeries.vue +16 -3
- package/src/ChartContainer.vue +14 -8
- package/src/LineSeries.vue +16 -3
- package/src/ui/YLabel.vue +28 -27
|
@@ -1,75 +1,102 @@
|
|
|
1
|
-
import { normalizeTime as
|
|
2
|
-
function
|
|
3
|
-
return "x" in
|
|
1
|
+
import { normalizeTime as T } from "../utils/time.js";
|
|
2
|
+
function s(a) {
|
|
3
|
+
return "x" in a ? a.x : T(a.time);
|
|
4
4
|
}
|
|
5
|
-
const
|
|
6
|
-
function
|
|
7
|
-
if (
|
|
8
|
-
const r = Object.keys(
|
|
9
|
-
return r.length !== e.length ? !1 : r.every((n) => n === "time" ||
|
|
5
|
+
const D = { len: 0, firstTime: null, lastTime: null, data: null }, A = 20;
|
|
6
|
+
function o(a, t) {
|
|
7
|
+
if (s(a) !== s(t)) return !1;
|
|
8
|
+
const r = Object.keys(a), e = Object.keys(t);
|
|
9
|
+
return r.length !== e.length ? !1 : r.every((n) => n === "time" || a[n] === t[n]);
|
|
10
10
|
}
|
|
11
|
-
function
|
|
12
|
-
if (!
|
|
13
|
-
for (let r = 0; r <
|
|
14
|
-
if (
|
|
11
|
+
function y(a, t) {
|
|
12
|
+
if (!a || a.length !== t.length || a === t) return !1;
|
|
13
|
+
for (let r = 0; r < t.length - 1; r++)
|
|
14
|
+
if (a[r] !== t[r] && !o(a[r], t[r]))
|
|
15
15
|
return !1;
|
|
16
16
|
return !0;
|
|
17
17
|
}
|
|
18
|
-
function
|
|
19
|
-
if (!
|
|
18
|
+
function S(a, t, r) {
|
|
19
|
+
if (!a || a.length === 0 || r <= 0 || a.length !== t.length - r) return !1;
|
|
20
20
|
for (let e = 1; e < r; e++)
|
|
21
|
-
if (
|
|
22
|
-
if (
|
|
23
|
-
for (let e = 0; e <
|
|
24
|
-
if (!
|
|
21
|
+
if (s(t[e - 1]) >= s(t[e])) return !1;
|
|
22
|
+
if (s(t[r - 1]) >= s(a[0])) return !1;
|
|
23
|
+
for (let e = 0; e < a.length; e++)
|
|
24
|
+
if (!o(a[e], t[r + e])) return !1;
|
|
25
25
|
return !0;
|
|
26
26
|
}
|
|
27
|
-
function
|
|
28
|
-
|
|
27
|
+
function m(a, t, r) {
|
|
28
|
+
if (!a || a === t) return "unchanged";
|
|
29
|
+
const e = a.length - r;
|
|
30
|
+
if (e <= 0 || e > t.length) return null;
|
|
31
|
+
for (let l = 0; l < e - 1; l++) {
|
|
32
|
+
const u = a[r + l];
|
|
33
|
+
if (u !== t[l] && !o(u, t[l]))
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
for (let l = e; l < t.length; l++)
|
|
37
|
+
if (s(t[l - 1]) >= s(t[l])) return null;
|
|
38
|
+
const n = a[a.length - 1], i = t[e - 1];
|
|
39
|
+
return n === i || o(n, i) ? "unchanged" : s(n) !== s(i) ? null : "lastEdited";
|
|
29
40
|
}
|
|
30
|
-
function b(
|
|
31
|
-
|
|
32
|
-
if (t.length === 0) return [{ data: [] }];
|
|
33
|
-
const a = t[0];
|
|
34
|
-
return Array.isArray(a) || A(a) ? t.map((r) => Array.isArray(r) ? { data: r } : r) : [{ data: t }];
|
|
41
|
+
function b(a) {
|
|
42
|
+
return typeof a == "object" && a !== null && !Array.isArray(a) && Array.isArray(a.data);
|
|
35
43
|
}
|
|
36
|
-
function L(
|
|
37
|
-
|
|
38
|
-
a.
|
|
44
|
+
function L(a) {
|
|
45
|
+
if (!Array.isArray(a)) return [a];
|
|
46
|
+
if (a.length === 0) return [{ data: [] }];
|
|
47
|
+
const t = a[0];
|
|
48
|
+
return Array.isArray(t) || b(t) ? a.map((r) => Array.isArray(r) ? { data: r } : r) : [{ data: a }];
|
|
49
|
+
}
|
|
50
|
+
function k(a) {
|
|
51
|
+
const { chart: t, id: r } = a, e = L(a.data), n = e.length > 1 ? e.map((l, u) => l.label ?? `Series ${u + 1}`) : [e[0].label];
|
|
52
|
+
t.setSeriesLabels(r, n), t.setLayerColors(
|
|
39
53
|
r,
|
|
40
54
|
e.map((l) => l.color)
|
|
41
55
|
);
|
|
42
|
-
const
|
|
43
|
-
return
|
|
56
|
+
const i = [];
|
|
57
|
+
return t.batch(() => {
|
|
44
58
|
for (let l = 0; l < e.length; l++)
|
|
45
|
-
|
|
46
|
-
chart:
|
|
59
|
+
i[l] = E({
|
|
60
|
+
chart: t,
|
|
47
61
|
id: r,
|
|
48
62
|
data: e[l].data,
|
|
49
|
-
prev:
|
|
63
|
+
prev: a.prev[l] ?? D,
|
|
50
64
|
layerIndex: l
|
|
51
65
|
});
|
|
52
|
-
}),
|
|
66
|
+
}), i;
|
|
53
67
|
}
|
|
54
|
-
function
|
|
55
|
-
const { chart:
|
|
68
|
+
function E(a) {
|
|
69
|
+
const { chart: t, id: r, data: e, prev: n, layerIndex: i } = a;
|
|
56
70
|
if (e.length === 0)
|
|
57
|
-
return
|
|
71
|
+
return t.setSeriesData(r, [], i), { len: 0, firstTime: null, lastTime: null, data: [] };
|
|
58
72
|
if (n.data === e && n.len === e.length) return n;
|
|
59
|
-
const l =
|
|
73
|
+
const l = s(e[0]), u = s(e[e.length - 1]);
|
|
60
74
|
if (typeof l == "string")
|
|
61
|
-
return e.length === n.len &&
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
75
|
+
return e.length === n.len && y(n.data, e) ? t.updateData(r, e[e.length - 1], i) : t.setSeriesData(r, e, i), { len: e.length, firstTime: l, lastTime: u, data: e };
|
|
76
|
+
const d = n.firstTime !== null && n.firstTime !== l, f = e.length - n.len, p = n.lastTime !== null && n.lastTime !== u, c = d && f === 0 && p ? m(n.data, e, 1) : null;
|
|
77
|
+
if (c !== null)
|
|
78
|
+
t.batch(() => {
|
|
79
|
+
c === "lastEdited" && t.updateData(r, e[e.length - 2], i), t.appendData(r, e[e.length - 1], i), t.keepLast(r, e.length, i);
|
|
80
|
+
});
|
|
81
|
+
else if (S(n.data, e, f))
|
|
82
|
+
t.prependData(r, e.slice(0, f), i);
|
|
83
|
+
else if (n.len === 0 || e.length < n.len || f > A || d)
|
|
84
|
+
t.setSeriesData(r, e, i);
|
|
85
|
+
else if (e.length === n.len)
|
|
86
|
+
y(n.data, e) ? t.updateData(r, e[e.length - 1], i) : t.setSeriesData(r, e, i);
|
|
87
|
+
else {
|
|
88
|
+
const g = m(n.data, e, 0);
|
|
89
|
+
g === null ? t.setSeriesData(r, e, i) : t.batch(() => {
|
|
90
|
+
g === "lastEdited" && t.updateData(r, e[n.len - 1], i);
|
|
91
|
+
for (let h = n.len; h < e.length; h++)
|
|
92
|
+
t.appendData(r, e[h], i);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return { len: e.length, firstTime: l, lastTime: u, data: e };
|
|
69
96
|
}
|
|
70
97
|
export {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
98
|
+
D as EMPTY_SYNC_STATE,
|
|
99
|
+
k as syncLayers,
|
|
100
|
+
E as syncSeriesLayer,
|
|
101
|
+
L as toLayers
|
|
75
102
|
};
|
|
@@ -3,7 +3,7 @@ import { XScale as T } from "../scales/x-scale.js";
|
|
|
3
3
|
import { YScale as k } from "../scales/y-scale.js";
|
|
4
4
|
import { decimateCandles as C, decimateLinear as R } from "./decimate.js";
|
|
5
5
|
import { hitTest as b, computeSnapCenter as E, computePan as I, computeResize as $ } from "./interactions.js";
|
|
6
|
-
import { computeWindowGeometry as M, renderBackground as L, renderMiniCandlestick as
|
|
6
|
+
import { computeWindowGeometry as M, renderBackground as L, renderMiniCandlestick as P, renderMiniBar as V, renderMiniLine as N } 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 {
|
|
@@ -61,16 +61,16 @@ class G {
|
|
|
61
61
|
#$;
|
|
62
62
|
#M;
|
|
63
63
|
#L;
|
|
64
|
-
#
|
|
64
|
+
#P;
|
|
65
65
|
#y;
|
|
66
66
|
#V;
|
|
67
67
|
constructor(t) {
|
|
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.#n = document.createElement("div"), this.#n.dataset.chartNavigatorOverlay = "", this.#n.style.cssText = "position:absolute;left:0;top:0;height:100%;pointer-events:none;", this.#u = document.createElement("div"), this.#u.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 = y(), this.#c = y(), this.#n.append(this.#u, this.#r, this.#l, this.#a, this.#c), this.#s.appendChild(this.#n), 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.#L = (s) => this.#U(s), this.#
|
|
71
|
+
this.#z = e, this.#n = document.createElement("div"), this.#n.dataset.chartNavigatorOverlay = "", this.#n.style.cssText = "position:absolute;left:0;top:0;height:100%;pointer-events:none;", this.#u = document.createElement("div"), this.#u.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 = y(), this.#c = y(), this.#n.append(this.#u, this.#r, this.#l, this.#a, this.#c), this.#s.appendChild(this.#n), 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.#L = (s) => this.#U(s), this.#P = (s) => this.#J(s), this.#y = (s) => this.#K(s), this.#V = () => {
|
|
72
72
|
this.#g === null && (this.#t.style.cursor = "default");
|
|
73
|
-
}, this.#t.addEventListener("pointerdown", this.#L), this.#t.addEventListener("pointermove", this.#
|
|
73
|
+
}, this.#t.addEventListener("pointerdown", this.#L), this.#t.addEventListener("pointermove", this.#P), this.#t.addEventListener("pointerup", this.#y), this.#t.addEventListener("pointercancel", this.#y), this.#t.addEventListener("pointerleave", this.#V), 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.#$), this.#e.off("tickFrame", this.#M), this.#t.removeEventListener("pointerdown", this.#L), this.#t.removeEventListener("pointermove", this.#
|
|
103
|
+
this.#e.off("viewportChange", this.#I), this.#e.off("overlayChange", this.#$), this.#e.off("tickFrame", this.#M), this.#t.removeEventListener("pointerdown", this.#L), this.#t.removeEventListener("pointermove", this.#P), this.#t.removeEventListener("pointerup", this.#y), this.#t.removeEventListener("pointercancel", this.#y), this.#t.removeEventListener("pointerleave", this.#V), this.#E.disconnect(), this.#p !== null && (cancelAnimationFrame(this.#p), this.#p = null), this.#t.remove(), this.#n.remove();
|
|
104
104
|
}
|
|
105
105
|
/** CSS pixel height of the strip — theme default unless overridden. */
|
|
106
106
|
get height() {
|
|
@@ -158,7 +158,7 @@ class G {
|
|
|
158
158
|
/** Keep scales synced with current data bounds + size. Safe to call ahead of
|
|
159
159
|
* the first RAF-driven render — pointer handlers rely on this so hit-tests
|
|
160
160
|
* compute against primed scales even on the opening gesture. */
|
|
161
|
-
#
|
|
161
|
+
#N() {
|
|
162
162
|
const t = this.#b(), e = this.#Z();
|
|
163
163
|
return this.#d.update(t, this.#i, this.#h), this.#A.update(e, this.#m, this.#h), { xRange: t, yRange: e };
|
|
164
164
|
}
|
|
@@ -172,7 +172,7 @@ class G {
|
|
|
172
172
|
return this.#g !== null ? this.#e.getVisibleRange() : this.#e.getAnimationState().xRange;
|
|
173
173
|
}
|
|
174
174
|
#S() {
|
|
175
|
-
return this.#
|
|
175
|
+
return this.#N(), M(this.#d, this.#Y(), this.#b());
|
|
176
176
|
}
|
|
177
177
|
#H() {
|
|
178
178
|
const t = this.#b(), e = t.to - t.from;
|
|
@@ -237,7 +237,7 @@ class G {
|
|
|
237
237
|
// --- render ------------------------------------------------------------
|
|
238
238
|
#_() {
|
|
239
239
|
if (this.#i <= 0 || this.#m <= 0) return;
|
|
240
|
-
const t = this.#e.getTheme().navigator, { xRange: e } = this.#
|
|
240
|
+
const t = this.#e.getTheme().navigator, { xRange: e } = this.#N();
|
|
241
241
|
this.#X();
|
|
242
242
|
const i = this.#z, s = this.#t.width, n = this.#t.height;
|
|
243
243
|
i.save(), i.clearRect(0, 0, s, n), i.scale(this.#h, this.#h);
|
|
@@ -254,12 +254,12 @@ class G {
|
|
|
254
254
|
this.#o !== null && (this.#o.tick(performance.now()), l = this.#o.current, this.#o.animating ? this.#f() : this.#o = null);
|
|
255
255
|
const r = Math.max(1, Math.round(this.#i)), a = this.#x;
|
|
256
256
|
if (a.type === "candlestick")
|
|
257
|
-
|
|
257
|
+
P(h, this.#q(a.points, r, C), l);
|
|
258
258
|
else {
|
|
259
259
|
const c = f(a), g = c.length === 1, u = this.#R >= 0 ? this.#R : c.length - 1;
|
|
260
260
|
for (let d = 0; d < c.length; d++) {
|
|
261
261
|
const p = this.#q(c[d], r, R), m = d === u ? l : 1;
|
|
262
|
-
a.type === "bar" ? V(h, p, m) :
|
|
262
|
+
a.type === "bar" ? V(h, p, m) : N(h, p, g, m);
|
|
263
263
|
}
|
|
264
264
|
}
|
|
265
265
|
i.restore(), this.#G(t, e);
|
|
@@ -277,7 +277,7 @@ class G {
|
|
|
277
277
|
this.#_();
|
|
278
278
|
return;
|
|
279
279
|
}
|
|
280
|
-
const i = this.#e.getTheme().navigator, { xRange: s } = this.#
|
|
280
|
+
const i = this.#e.getTheme().navigator, { xRange: s } = this.#N();
|
|
281
281
|
this.#G(i, s);
|
|
282
282
|
}
|
|
283
283
|
/** Decimate `points` into `buckets`, reusing the cached result while the
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
const d = (e) => -new Date(e).getTimezoneOffset() * 6e4, M = () => 0, l = /* @__PURE__ */ new Map();
|
|
2
|
+
function F(e) {
|
|
3
|
+
const t = U(e.timeZone);
|
|
4
|
+
return e.interval < 864e5 ? T(e, t) : v(e, t);
|
|
5
|
+
}
|
|
6
|
+
function T(e, t) {
|
|
7
|
+
const { from: n, to: o, interval: i, max: r } = e, a = [];
|
|
8
|
+
if (!(Number.isFinite(n) && Number.isFinite(o) && n <= o)) return a;
|
|
9
|
+
let u = t(n), f = n, c = y({ start: n, offset: u, interval: i });
|
|
10
|
+
for (; a.length < r; ) {
|
|
11
|
+
const h = Math.min(c, o);
|
|
12
|
+
if (t(h) !== u) {
|
|
13
|
+
const m = g({ from: f, to: h, offsetAt: t });
|
|
14
|
+
u = t(m), f = m, c = y({ start: m, offset: u, interval: i });
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
if (c > o) break;
|
|
18
|
+
a.push(c), f = c, c += i;
|
|
19
|
+
}
|
|
20
|
+
return a;
|
|
21
|
+
}
|
|
22
|
+
function y(e) {
|
|
23
|
+
const { start: t, offset: n, interval: o } = e;
|
|
24
|
+
return Math.ceil((t + n) / o) * o - n;
|
|
25
|
+
}
|
|
26
|
+
function g(e) {
|
|
27
|
+
const { offsetAt: t } = e, n = t(e.from);
|
|
28
|
+
let o = e.from, i = e.to;
|
|
29
|
+
for (; i - o > 1; ) {
|
|
30
|
+
const r = Math.floor((o + i) / 2);
|
|
31
|
+
t(r) === n ? o = r : i = r;
|
|
32
|
+
}
|
|
33
|
+
return i;
|
|
34
|
+
}
|
|
35
|
+
function v(e, t) {
|
|
36
|
+
const { from: n, to: o, interval: i, max: r } = e, a = p(i, new Date(n + t(n))), u = [];
|
|
37
|
+
for (let f = 0; u.length < r; f++) {
|
|
38
|
+
const c = C(a(f), t);
|
|
39
|
+
if (!(c <= o)) break;
|
|
40
|
+
c >= n && u.push(c);
|
|
41
|
+
}
|
|
42
|
+
return u;
|
|
43
|
+
}
|
|
44
|
+
function p(e, t) {
|
|
45
|
+
const n = t.getUTCFullYear(), o = t.getUTCMonth(), i = t.getUTCDate();
|
|
46
|
+
if (e >= 31536e6) {
|
|
47
|
+
const r = Math.round(e / 31536e6), a = Math.floor(n / r) * r;
|
|
48
|
+
return (u) => s({ year: a + u * r, month: 0, day: 1 });
|
|
49
|
+
}
|
|
50
|
+
if (e >= 2592e6) {
|
|
51
|
+
const r = e >= 7776e6 ? 3 : 1, a = Math.floor(o / r) * r;
|
|
52
|
+
return (u) => s({ year: n, month: a + u * r, day: 1 });
|
|
53
|
+
}
|
|
54
|
+
if (e >= 6048e5) {
|
|
55
|
+
const r = (t.getUTCDay() + 6) % 7;
|
|
56
|
+
return (a) => s({ year: n, month: o, day: i - r + a * 7 });
|
|
57
|
+
}
|
|
58
|
+
return (r) => s({ year: n, month: o, day: i + r });
|
|
59
|
+
}
|
|
60
|
+
function s(e) {
|
|
61
|
+
const t = /* @__PURE__ */ new Date(0);
|
|
62
|
+
return t.setUTCFullYear(e.year, e.month, e.day), t.getTime();
|
|
63
|
+
}
|
|
64
|
+
function C(e, t) {
|
|
65
|
+
const n = e - t(e - 864e5), o = e - t(e + 864e5), i = n + t(n) === e, r = o + t(o) === e;
|
|
66
|
+
return i && r ? Math.min(n, o) : r ? o : n;
|
|
67
|
+
}
|
|
68
|
+
function U(e) {
|
|
69
|
+
if (e === void 0) return d;
|
|
70
|
+
if (e === "UTC") return M;
|
|
71
|
+
let t = l.get(e);
|
|
72
|
+
return t === void 0 && (t = D(e), l.set(e, t)), t;
|
|
73
|
+
}
|
|
74
|
+
function D(e) {
|
|
75
|
+
const t = new Intl.DateTimeFormat("en-US", {
|
|
76
|
+
timeZone: e,
|
|
77
|
+
hourCycle: "h23",
|
|
78
|
+
year: "numeric",
|
|
79
|
+
month: "numeric",
|
|
80
|
+
day: "numeric",
|
|
81
|
+
hour: "numeric",
|
|
82
|
+
minute: "numeric",
|
|
83
|
+
second: "numeric"
|
|
84
|
+
}), n = /* @__PURE__ */ new Map();
|
|
85
|
+
return (o) => {
|
|
86
|
+
const i = Math.floor(o / 9e5);
|
|
87
|
+
let r = n.get(i);
|
|
88
|
+
return r === void 0 && (n.size >= 4096 && n.clear(), r = w(t, i * 9e5), n.set(i, r)), r;
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function w(e, t) {
|
|
92
|
+
const n = { year: 0, month: 1, day: 1, hour: 0, minute: 0, second: 0 };
|
|
93
|
+
for (const o of e.formatToParts(t))
|
|
94
|
+
o.type in n && (n[o.type] = Number(o.value));
|
|
95
|
+
return s({ year: n.year, month: n.month - 1, day: n.day }) + ((n.hour * 60 + n.minute) * 60 + n.second) * 1e3 - t;
|
|
96
|
+
}
|
|
97
|
+
export {
|
|
98
|
+
F as timeTicks
|
|
99
|
+
};
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { clamp as
|
|
1
|
+
import { clamp as f } from "../utils/math.js";
|
|
2
2
|
import { crispCenterOffset as v } from "../utils/pixel-grid.js";
|
|
3
|
-
import { formatTime as
|
|
3
|
+
import { formatTime as p, niceTimeIntervals as g } from "../utils/time.js";
|
|
4
4
|
import { AxisTickTracker as I } from "./tick-tracker.js";
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
import { timeTicks as k } from "./time-ticks.js";
|
|
6
|
+
import { pickNiceCeiling as b, pickNiceFloor as y } from "./y-scale.js";
|
|
7
|
+
const o = 50, T = 80, h = 0.3, u = 0.2;
|
|
8
|
+
class w {
|
|
8
9
|
/**
|
|
9
10
|
* Shared fade state for time ticks. Grid lines (canvas) and DOM tick labels
|
|
10
11
|
* read opacity from the same tracker, so a tick fading in/out looks
|
|
@@ -44,20 +45,20 @@ class W {
|
|
|
44
45
|
return this.labelCountHintValue;
|
|
45
46
|
}
|
|
46
47
|
get minLabelSpacing() {
|
|
47
|
-
return this.minSpacingValue ??
|
|
48
|
+
return this.minSpacingValue ?? T;
|
|
48
49
|
}
|
|
49
50
|
update(t, i, s, e) {
|
|
50
51
|
if (this.from = t.from, this.to = t.to, this.width = i, this.pixelRatio = s, this.type === "time" && e !== void 0 && e > 0) {
|
|
51
|
-
const r =
|
|
52
|
+
const r = m(e);
|
|
52
53
|
this.lastBucketKey !== null && this.lastBucketKey !== r && this.resetHysteresis(), this.lastBucketKey = r, this.dataInterval = e;
|
|
53
54
|
}
|
|
54
55
|
this.resolveInterval();
|
|
55
56
|
}
|
|
56
57
|
setLabelCount(t) {
|
|
57
|
-
this.labelCountHintValue =
|
|
58
|
+
this.labelCountHintValue = x(t), this.resetHysteresis(), this.resolveInterval();
|
|
58
59
|
}
|
|
59
60
|
setMinSpacing(t) {
|
|
60
|
-
this.minSpacingValue =
|
|
61
|
+
this.minSpacingValue = V(t), this.resetHysteresis(), this.resolveInterval();
|
|
61
62
|
}
|
|
62
63
|
setFormat(t) {
|
|
63
64
|
this.customFormat = t;
|
|
@@ -83,8 +84,8 @@ class W {
|
|
|
83
84
|
/** Band spacing on a category axis; an omitted or invalid side takes its default. */
|
|
84
85
|
setBandPadding(t) {
|
|
85
86
|
this.bandPadding = {
|
|
86
|
-
inner:
|
|
87
|
-
outer:
|
|
87
|
+
inner: c({ value: t?.inner, fallback: h, max: 1 }),
|
|
88
|
+
outer: c({ value: t?.outer, fallback: u, max: 1 / 0 })
|
|
88
89
|
};
|
|
89
90
|
}
|
|
90
91
|
getBandPadding() {
|
|
@@ -110,7 +111,7 @@ class W {
|
|
|
110
111
|
*/
|
|
111
112
|
snapToBand(t) {
|
|
112
113
|
const i = this.categories.length;
|
|
113
|
-
return this.type !== "category" || i === 0 ? t :
|
|
114
|
+
return this.type !== "category" || i === 0 ? t : f(Math.round(t), 0, i - 1);
|
|
114
115
|
}
|
|
115
116
|
/**
|
|
116
117
|
* An internal coordinate in the axis domain: the category name at that
|
|
@@ -151,7 +152,7 @@ class W {
|
|
|
151
152
|
*/
|
|
152
153
|
formatX(t, i) {
|
|
153
154
|
const s = this.domainValue(t);
|
|
154
|
-
return this.customFormat ? this.customFormat(s, i) : this.type === "category" ? String(s) : this.type === "numeric" ?
|
|
155
|
+
return this.customFormat ? this.customFormat(s, i) : this.type === "category" ? String(s) : this.type === "numeric" ? M({ value: t, step: i, locale: this.localeValue }) : p(t, i, { locale: this.localeValue, timeZone: this.timeZoneValue });
|
|
155
156
|
}
|
|
156
157
|
/**
|
|
157
158
|
* Format a data point's X for a tooltip or info-bar header. Like
|
|
@@ -197,7 +198,9 @@ class W {
|
|
|
197
198
|
return Math.max(1, Math.round(this.barWidthMedia(t) * this.pixelRatio));
|
|
198
199
|
}
|
|
199
200
|
/**
|
|
200
|
-
*
|
|
201
|
+
* "Nice" tick values. A time axis puts them on calendar boundaries in the
|
|
202
|
+
* installed time zone (Jan 1, the 1st of a month, midnight), so their gaps
|
|
203
|
+
* vary; `tickInterval` is the tier's nominal span. Resolution uses the cached
|
|
201
204
|
* `dataInterval` set by `update()`; callers that bypass `update()` can
|
|
202
205
|
* still pass it here for back-compat.
|
|
203
206
|
*/
|
|
@@ -209,11 +212,14 @@ class W {
|
|
|
209
212
|
}
|
|
210
213
|
if (this.type === "category") return this.bandTicks();
|
|
211
214
|
if (this.type === "time" && this.dataInterval !== t) {
|
|
212
|
-
const a =
|
|
215
|
+
const a = m(t);
|
|
213
216
|
this.lastBucketKey !== null && this.lastBucketKey !== a && this.resetHysteresis(), this.lastBucketKey = a, this.dataInterval = t, this.resolveInterval();
|
|
214
217
|
}
|
|
215
218
|
if (this.resolvedInterval == null) return { ticks: [], tickInterval: 0 };
|
|
216
|
-
const i = this.resolvedInterval
|
|
219
|
+
const i = this.resolvedInterval;
|
|
220
|
+
if (this.type === "time")
|
|
221
|
+
return { ticks: k({ from: this.from, to: this.to, interval: i, timeZone: this.timeZoneValue, max: o }), tickInterval: i };
|
|
222
|
+
const s = Math.ceil(this.from / i) * i, e = Math.max(0, Math.min(o, Math.floor((this.to - s) / i) + 1)), r = [];
|
|
217
223
|
for (let a = 0; a < e; a++) r.push(s + a * i);
|
|
218
224
|
return { ticks: r, tickInterval: i };
|
|
219
225
|
}
|
|
@@ -244,7 +250,7 @@ class W {
|
|
|
244
250
|
this.resolvedInterval = null;
|
|
245
251
|
return;
|
|
246
252
|
}
|
|
247
|
-
const t =
|
|
253
|
+
const t = g(this.dataInterval), i = this.to - this.from, s = i / this.width * this.minLabelSpacing;
|
|
248
254
|
let e;
|
|
249
255
|
if (this.labelCountHint != null) {
|
|
250
256
|
const l = Math.max(1, this.labelCountHint);
|
|
@@ -252,8 +258,8 @@ class W {
|
|
|
252
258
|
} else
|
|
253
259
|
e = s;
|
|
254
260
|
if (this.lastInterval != null && this.lastWant != null && t.includes(this.lastInterval)) {
|
|
255
|
-
const l = e >= this.lastWant * 0.8 && e <= this.lastWant * 1.25,
|
|
256
|
-
if (l &&
|
|
261
|
+
const l = e >= this.lastWant * 0.8 && e <= this.lastWant * 1.25, d = this.lastInterval >= s;
|
|
262
|
+
if (l && d) {
|
|
257
263
|
this.resolvedInterval = this.lastInterval;
|
|
258
264
|
return;
|
|
259
265
|
}
|
|
@@ -289,7 +295,7 @@ class W {
|
|
|
289
295
|
}
|
|
290
296
|
let r = b(e);
|
|
291
297
|
if (this.labelCountHint != null && this.countTicks(r) < this.labelCountHint) {
|
|
292
|
-
const a =
|
|
298
|
+
const a = y(e);
|
|
293
299
|
a >= i && (r = a);
|
|
294
300
|
}
|
|
295
301
|
this.resolvedInterval = r, this.lastInterval = r, this.lastWant = e;
|
|
@@ -300,23 +306,23 @@ class W {
|
|
|
300
306
|
return Math.max(0, Math.floor((this.to - i) / t) + 1);
|
|
301
307
|
}
|
|
302
308
|
}
|
|
303
|
-
function
|
|
309
|
+
function m(n) {
|
|
304
310
|
return n < 6e4 ? 0 : n < 36e5 ? 1 : n < 864e5 ? 2 : 3;
|
|
305
311
|
}
|
|
306
|
-
function
|
|
307
|
-
const { value: t, step: i, locale: s } = n, e = Number.isFinite(i) && i > 0 ? -Math.floor(Math.log10(i)) : 2, r =
|
|
312
|
+
function M(n) {
|
|
313
|
+
const { value: t, step: i, locale: s } = n, e = Number.isFinite(i) && i > 0 ? -Math.floor(Math.log10(i)) : 2, r = f(e, 0, 20);
|
|
308
314
|
return t.toLocaleString(s ?? "en-US", { maximumFractionDigits: r });
|
|
309
315
|
}
|
|
310
|
-
function
|
|
316
|
+
function c(n) {
|
|
311
317
|
const { value: t, fallback: i, max: s } = n;
|
|
312
318
|
return t === void 0 || !Number.isFinite(t) || t < 0 ? i : Math.min(t, s);
|
|
313
319
|
}
|
|
314
|
-
function
|
|
320
|
+
function x(n) {
|
|
315
321
|
return typeof n == "number" && Number.isFinite(n) && n >= 2 ? Math.floor(n) : null;
|
|
316
322
|
}
|
|
317
|
-
function
|
|
323
|
+
function V(n) {
|
|
318
324
|
return typeof n == "number" && Number.isFinite(n) && n > 0 ? n : null;
|
|
319
325
|
}
|
|
320
326
|
export {
|
|
321
|
-
|
|
327
|
+
w as XScale
|
|
322
328
|
};
|