@wick-charts/vue 0.8.0 → 0.8.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 +2 -0
- package/dist/BandSeries.vue.js +43 -32
- package/dist/BarSeries.vue.js +44 -33
- package/dist/CandlestickSeries.vue.js +24 -23
- package/dist/ChartContainer.vue.js +1 -1
- package/dist/HeatmapSeries.vue.js +21 -20
- package/dist/LineSeries.vue.js +43 -32
- package/dist/PieSeries.vue.js +25 -24
- package/dist/composables.js +63 -36
- package/dist/core/dist/axis/dom-labels.js +64 -53
- package/dist/core/dist/chart/badge-animator.js +38 -28
- package/dist/core/dist/chart/options.js +1 -1
- package/dist/core/dist/chart/y-target.js +38 -32
- package/dist/core/dist/chart.js +852 -709
- package/dist/core/dist/data/decimation.js +52 -51
- package/dist/core/dist/data/store.js +38 -20
- package/dist/core/dist/data/sync.js +97 -59
- package/dist/core/dist/navigator/controller.js +11 -11
- package/dist/core/dist/perf/perf-hud.js +3 -3
- package/dist/core/dist/scales/tick-tracker.js +2 -1
- 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 +83 -83
- package/dist/core/dist/series/bar.js +240 -211
- package/dist/core/dist/series/base-multi-layer.js +288 -240
- package/dist/core/dist/series/candlestick.js +168 -168
- package/dist/core/dist/series/line.js +545 -455
- package/dist/core/dist/snapshots.js +109 -86
- package/dist/core/dist/utils/point.js +4 -0
- package/dist/core/dist/utils/time.js +42 -26
- package/dist/index.d.ts +67 -28
- package/dist/ui/InfoBar.vue.js +69 -69
- package/dist/ui/NumberFlow.vue.js +36 -27
- package/dist/ui/Tooltip.vue.js +128 -131
- package/dist/ui/YLabel.vue.js +47 -47
- package/package.json +2 -2
- package/src/BandSeries.vue +20 -5
- package/src/BarSeries.vue +20 -5
- package/src/CandlestickSeries.vue +5 -4
- package/src/ChartContainer.vue +15 -9
- package/src/HeatmapSeries.vue +5 -4
- package/src/LineSeries.vue +20 -5
- package/src/PieSeries.vue +6 -5
- package/src/composables.ts +64 -1
- package/src/ui/InfoBar.vue +11 -3
- package/src/ui/NumberFlow.vue +17 -3
- package/src/ui/Tooltip.vue +23 -12
- package/src/ui/YLabel.vue +28 -27
|
@@ -1,73 +1,74 @@
|
|
|
1
|
-
function
|
|
2
|
-
return t.length <=
|
|
1
|
+
function k(t, n) {
|
|
2
|
+
return t.length <= n ? t : D(
|
|
3
3
|
t,
|
|
4
|
-
|
|
5
|
-
(
|
|
6
|
-
(
|
|
4
|
+
n,
|
|
5
|
+
(h) => h.time,
|
|
6
|
+
(h) => h.value
|
|
7
7
|
);
|
|
8
8
|
}
|
|
9
|
-
function
|
|
10
|
-
if (t.length <=
|
|
11
|
-
const
|
|
12
|
-
for (let
|
|
13
|
-
const
|
|
9
|
+
function x(t, n) {
|
|
10
|
+
if (t.length <= n) return t;
|
|
11
|
+
const h = Math.ceil(t.length / n), a = [];
|
|
12
|
+
for (let l = 0; l < t.length; l += h) {
|
|
13
|
+
const o = Math.min(l + h, t.length);
|
|
14
14
|
let s = -1 / 0, i = 1 / 0, c = 0;
|
|
15
|
-
for (let
|
|
16
|
-
t[
|
|
17
|
-
|
|
18
|
-
time: t[
|
|
19
|
-
open: t[
|
|
15
|
+
for (let e = l; e < o; e++)
|
|
16
|
+
t[e].high > s && (s = t[e].high), t[e].low < i && (i = t[e].low), c += t[e].volume ?? 0;
|
|
17
|
+
a.push({
|
|
18
|
+
time: t[l].time,
|
|
19
|
+
open: t[l].open,
|
|
20
20
|
high: s,
|
|
21
21
|
low: i,
|
|
22
|
-
close: t[
|
|
22
|
+
close: t[o - 1].close,
|
|
23
23
|
volume: c
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
|
-
return
|
|
26
|
+
return a;
|
|
27
27
|
}
|
|
28
|
-
function
|
|
29
|
-
if (t.length <=
|
|
30
|
-
const
|
|
31
|
-
for (let
|
|
32
|
-
const s = Math.min(
|
|
33
|
-
let i = 1 / 0, c = -1 / 0,
|
|
34
|
-
for (let
|
|
35
|
-
const
|
|
36
|
-
if (!Number.isFinite(
|
|
37
|
-
|
|
28
|
+
function L(t, n) {
|
|
29
|
+
if (t.length <= n || n < 3) return t;
|
|
30
|
+
const h = t.length - 1, a = Math.ceil((t.length - 2) / (n - 2)), l = [t[0]];
|
|
31
|
+
for (let o = 1; o < h; o += a) {
|
|
32
|
+
const s = Math.min(o + a, h);
|
|
33
|
+
let i = 1 / 0, c = -1 / 0, e = !1;
|
|
34
|
+
for (let m = o; m < s; m++) {
|
|
35
|
+
const r = t[m];
|
|
36
|
+
if (!Number.isFinite(r.low) || !Number.isFinite(r.high)) {
|
|
37
|
+
e = !0;
|
|
38
38
|
break;
|
|
39
39
|
}
|
|
40
|
-
|
|
40
|
+
r.low < i && (i = r.low), r.high > c && (c = r.high);
|
|
41
41
|
}
|
|
42
|
-
|
|
42
|
+
l.push(e ? { time: t[o].time, low: Number.NaN, high: Number.NaN } : { time: t[o].time, low: i, high: c });
|
|
43
43
|
}
|
|
44
|
-
return
|
|
44
|
+
return l.push(t[h]), l;
|
|
45
45
|
}
|
|
46
|
-
function
|
|
47
|
-
if (
|
|
48
|
-
const
|
|
46
|
+
function D(t, n, h, a) {
|
|
47
|
+
if (n >= t.length || n < 3) return t;
|
|
48
|
+
const l = [t[0]], o = (t.length - 2) / (n - 2);
|
|
49
49
|
let s = 0;
|
|
50
|
-
for (let i = 0; i <
|
|
51
|
-
const c = Math.floor((i + 1) *
|
|
52
|
-
let
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
f /= g, a /= g;
|
|
50
|
+
for (let i = 0; i < n - 2; i++) {
|
|
51
|
+
const c = Math.floor((i + 1) * o) + 1, e = Math.min(Math.floor((i + 2) * o) + 1, t.length - 1), m = h(t[s]), r = a(t[s]), p = Number.isFinite(r);
|
|
52
|
+
let g = 0, N = 0, b = 0;
|
|
53
|
+
const d = e, v = Math.min(Math.floor((i + 3) * o) + 1, t.length);
|
|
54
|
+
for (let u = d; u < v; u++) {
|
|
55
|
+
const f = a(t[u]);
|
|
56
|
+
Number.isFinite(f) && (g += h(t[u]), N += f, b++);
|
|
58
57
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
for (let
|
|
62
|
-
const
|
|
63
|
-
|
|
58
|
+
b > 0 ? (g /= b, N /= b) : (g = m, N = p ? r : 0);
|
|
59
|
+
let w = -1, M = c;
|
|
60
|
+
for (let u = c; u < e; u++) {
|
|
61
|
+
const f = a(t[u]);
|
|
62
|
+
if (!Number.isFinite(f)) continue;
|
|
63
|
+
const F = p ? Math.abs((m - g) * (f - r) - (m - h(t[u])) * (N - r)) : 0;
|
|
64
|
+
F > w && (w = F, M = u);
|
|
64
65
|
}
|
|
65
|
-
|
|
66
|
+
l.push(t[M]), s = M;
|
|
66
67
|
}
|
|
67
|
-
return
|
|
68
|
+
return l.push(t[t.length - 1]), l;
|
|
68
69
|
}
|
|
69
70
|
export {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
L as decimateBandData,
|
|
72
|
+
k as decimateLineData,
|
|
73
|
+
x as decimateOHLCData
|
|
73
74
|
};
|
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
import { EventEmitter as
|
|
1
|
+
import { EventEmitter as l } from "../events.js";
|
|
2
2
|
import { binarySearch as n } from "../utils/math.js";
|
|
3
|
-
class u extends
|
|
3
|
+
class u extends l {
|
|
4
4
|
data = [];
|
|
5
5
|
_visible = !0;
|
|
6
6
|
cachedRange = null;
|
|
7
7
|
cachedResult = [];
|
|
8
|
+
// Points `trimStart` dropped, oldest first. A view whose left edge is older
|
|
9
|
+
// than the kept head still draws them, so a rolling window slides out of view.
|
|
10
|
+
exited = [];
|
|
11
|
+
// Leftmost `from` read since the last trim: older exited points are out of every view since then.
|
|
12
|
+
lowestReadFrom = Number.POSITIVE_INFINITY;
|
|
13
|
+
appendedSinceTrim = 0;
|
|
8
14
|
// Dev-only, warn-once-per-instance: these two conditions are almost always
|
|
9
15
|
// a persistent bug in the caller's feed (not a one-off), so a per-call
|
|
10
16
|
// warning would just spam the console on every tick.
|
|
@@ -25,7 +31,7 @@ class u extends d {
|
|
|
25
31
|
}
|
|
26
32
|
!a && process.env.NODE_ENV !== "production" && !this.warnedUnsorted && (this.warnedUnsorted = !0, console.warn(
|
|
27
33
|
"[wick-charts] data is not sorted by time — re-sorting on every setData() call is wasted work. Sort your feed by `time` ascending before passing it in."
|
|
28
|
-
)), this.data = a ? t.slice() : [...t].sort((e,
|
|
34
|
+
)), this.data = a ? t.slice() : [...t].sort((e, s) => e.time - s.time), this.exited = [], this.appendedSinceTrim = 0, this.cachedRange = null, this.emit("update");
|
|
29
35
|
}
|
|
30
36
|
append(t) {
|
|
31
37
|
if (this.data.length > 0 && t.time <= this.data[this.data.length - 1].time) {
|
|
@@ -34,7 +40,7 @@ class u extends d {
|
|
|
34
40
|
)), this.updateLast(t);
|
|
35
41
|
return;
|
|
36
42
|
}
|
|
37
|
-
this.data.push(t), this.cachedRange = null, this.emit("update");
|
|
43
|
+
this.data.push(t), this.appendedSinceTrim++, this.cachedRange = null, this.emit("update");
|
|
38
44
|
}
|
|
39
45
|
updateLast(t) {
|
|
40
46
|
this.data.length !== 0 && (this.data[this.data.length - 1] = t, this.cachedRange = null, this.emit("update"));
|
|
@@ -47,16 +53,22 @@ class u extends d {
|
|
|
47
53
|
* is already validated and no re-sort is needed here. No-op on empty input.
|
|
48
54
|
*/
|
|
49
55
|
prepend(t) {
|
|
50
|
-
t.length !== 0 && (this.data = t.concat(this.data), this.cachedRange = null, this.emit("update"));
|
|
56
|
+
t.length !== 0 && (this.data = t.concat(this.data), this.exited = [], this.cachedRange = null, this.emit("update"));
|
|
51
57
|
}
|
|
52
58
|
trimStart(t) {
|
|
53
|
-
if (
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
if (t <= 0 || this.data.length === 0) return;
|
|
60
|
+
const a = this.data.slice(0, t), e = a.slice(Math.max(0, a.length - this.appendedSinceTrim)), s = e.length === a.length ? this.exited.concat(e) : e;
|
|
61
|
+
this.data = this.data.slice(t), this.exited = this.reachedByView(s), this.appendedSinceTrim = 0, this.lowestReadFrom = Number.POSITIVE_INFINITY, this.cachedRange = null, this.emit("update");
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The exited points the leftmost view read since the last trim needs to reach its left edge.
|
|
65
|
+
* None without a read, so a chart that does not paint (a hidden tab) does not pile them up.
|
|
66
|
+
*/
|
|
67
|
+
reachedByView(t) {
|
|
68
|
+
const a = this.lowestReadFrom, e = this.data[0];
|
|
69
|
+
if (e === void 0 || e.time <= a) return [];
|
|
70
|
+
const s = Math.max(0, n(t, a, (h) => h.time) - 1);
|
|
71
|
+
return t.slice(s);
|
|
60
72
|
}
|
|
61
73
|
getAll() {
|
|
62
74
|
return this.data;
|
|
@@ -64,8 +76,14 @@ class u extends d {
|
|
|
64
76
|
getVisibleData(t, a) {
|
|
65
77
|
if (this.data.length === 0) return [];
|
|
66
78
|
if (this.cachedRange && this.cachedRange.from === t && this.cachedRange.to === a) return this.cachedResult;
|
|
67
|
-
|
|
68
|
-
|
|
79
|
+
this.lowestReadFrom = Math.min(this.lowestReadFrom, t);
|
|
80
|
+
const e = Math.max(0, n(this.data, t, (i) => i.time) - 1), s = Math.min(this.data.length, n(this.data, a, (i) => i.time) + 1);
|
|
81
|
+
let h = this.data.slice(e, s);
|
|
82
|
+
if (e === 0 && this.data[0].time > t && this.exited.length > 0) {
|
|
83
|
+
const i = Math.max(0, n(this.exited, t, (r) => r.time) - 1), d = Math.min(this.exited.length, n(this.exited, a, (r) => r.time) + 1);
|
|
84
|
+
h = this.exited.slice(i, d).concat(h);
|
|
85
|
+
}
|
|
86
|
+
return this.cachedResult = h, this.cachedRange = { from: t, to: a }, h;
|
|
69
87
|
}
|
|
70
88
|
first() {
|
|
71
89
|
return this.data[0];
|
|
@@ -81,13 +99,13 @@ class u extends d {
|
|
|
81
99
|
}
|
|
82
100
|
findNearest(t, a) {
|
|
83
101
|
if (this.data.length === 0) return null;
|
|
84
|
-
const e = n(this.data, t, (
|
|
85
|
-
let
|
|
86
|
-
for (let
|
|
87
|
-
const
|
|
88
|
-
|
|
102
|
+
const e = n(this.data, t, (i) => i.time);
|
|
103
|
+
let s = null, h = a;
|
|
104
|
+
for (let i = Math.max(0, e - 1); i <= Math.min(this.data.length - 1, e + 1); i++) {
|
|
105
|
+
const d = Math.abs(this.data[i].time - t);
|
|
106
|
+
d <= h && (h = d, s = this.data[i]);
|
|
89
107
|
}
|
|
90
|
-
return
|
|
108
|
+
return s;
|
|
91
109
|
}
|
|
92
110
|
}
|
|
93
111
|
export {
|
|
@@ -1,75 +1,113 @@
|
|
|
1
|
-
import { normalizeTime as
|
|
2
|
-
function i(
|
|
3
|
-
return "x" in
|
|
1
|
+
import { normalizeTime as A } from "../utils/time.js";
|
|
2
|
+
function i(a) {
|
|
3
|
+
return "x" in a ? a.x : A(a.time);
|
|
4
4
|
}
|
|
5
|
-
const
|
|
6
|
-
function
|
|
7
|
-
if (i(
|
|
8
|
-
const
|
|
9
|
-
return
|
|
5
|
+
const S = { len: 0, firstTime: null, lastTime: null, data: null, last: null }, b = 20;
|
|
6
|
+
function o(a, t) {
|
|
7
|
+
if (i(a) !== i(t)) return !1;
|
|
8
|
+
const l = Object.keys(a), e = Object.keys(t);
|
|
9
|
+
return l.length !== e.length ? !1 : l.every((r) => r === "time" || a[r] === t[r]);
|
|
10
10
|
}
|
|
11
|
-
function
|
|
12
|
-
if (!
|
|
13
|
-
for (let
|
|
14
|
-
if (
|
|
11
|
+
function p(a, t) {
|
|
12
|
+
if (!a || a.length !== t.length || a === t) return !1;
|
|
13
|
+
for (let l = 0; l < t.length - 1; l++)
|
|
14
|
+
if (a[l] !== t[l] && !o(a[l], t[l]))
|
|
15
15
|
return !1;
|
|
16
16
|
return !0;
|
|
17
17
|
}
|
|
18
|
-
function
|
|
19
|
-
if (!
|
|
20
|
-
for (let e = 1; e <
|
|
21
|
-
if (i(
|
|
22
|
-
if (i(
|
|
23
|
-
for (let e = 0; e <
|
|
24
|
-
if (!
|
|
18
|
+
function E(a, t, l) {
|
|
19
|
+
if (!a || a.length === 0 || l <= 0 || a.length !== t.length - l) return !1;
|
|
20
|
+
for (let e = 1; e < l; e++)
|
|
21
|
+
if (i(t[e - 1]) >= i(t[e])) return !1;
|
|
22
|
+
if (i(t[l - 1]) >= i(a[0])) return !1;
|
|
23
|
+
for (let e = 0; e < a.length; e++)
|
|
24
|
+
if (!o(a[e], t[l + e])) return !1;
|
|
25
25
|
return !0;
|
|
26
26
|
}
|
|
27
|
-
function
|
|
28
|
-
|
|
27
|
+
function m(a, t, l) {
|
|
28
|
+
const e = a.data;
|
|
29
|
+
if (!e) return "unchanged";
|
|
30
|
+
if (e === t) return T(a, t) ? "lastEdited" : "unchanged";
|
|
31
|
+
const r = e.length - l;
|
|
32
|
+
if (r <= 0 || r > t.length) return null;
|
|
33
|
+
for (let u = 0; u < r - 1; u++) {
|
|
34
|
+
const f = e[l + u];
|
|
35
|
+
if (f !== t[u] && !o(f, t[u]))
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
for (let u = r; u < t.length; u++)
|
|
39
|
+
if (i(t[u - 1]) >= i(t[u])) return null;
|
|
40
|
+
const n = e[e.length - 1], s = t[r - 1];
|
|
41
|
+
return n === s || o(n, s) ? "unchanged" : i(n) !== i(s) ? null : "lastEdited";
|
|
29
42
|
}
|
|
30
|
-
function
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const a = t[0];
|
|
34
|
-
return Array.isArray(a) || A(a) ? t.map((r) => Array.isArray(r) ? { data: r } : r) : [{ data: t }];
|
|
43
|
+
function T(a, t) {
|
|
44
|
+
const l = a.last ?? null, e = t[a.len - 1];
|
|
45
|
+
return l === null || e === void 0 ? !1 : i(l) === i(e) && !o(l, e);
|
|
35
46
|
}
|
|
36
|
-
function
|
|
37
|
-
const
|
|
38
|
-
a.
|
|
39
|
-
|
|
40
|
-
|
|
47
|
+
function h(a) {
|
|
48
|
+
const t = a[a.length - 1];
|
|
49
|
+
return { len: a.length, firstTime: i(a[0]), lastTime: i(t), data: a, last: { ...t } };
|
|
50
|
+
}
|
|
51
|
+
function L(a) {
|
|
52
|
+
return typeof a == "object" && a !== null && !Array.isArray(a) && Array.isArray(a.data);
|
|
53
|
+
}
|
|
54
|
+
function v(a) {
|
|
55
|
+
if (!Array.isArray(a)) return [a];
|
|
56
|
+
if (a.length === 0) return [{ data: [] }];
|
|
57
|
+
const t = a[0];
|
|
58
|
+
return Array.isArray(t) || L(t) ? a.map((l) => Array.isArray(l) ? { data: l } : l) : [{ data: a }];
|
|
59
|
+
}
|
|
60
|
+
function j(a) {
|
|
61
|
+
const { chart: t, id: l } = a, e = v(a.data), r = e.length > 1 ? e.map((s, u) => s.label ?? `Series ${u + 1}`) : [e[0].label];
|
|
62
|
+
t.setSeriesLabels(l, r), t.setLayerColors(
|
|
63
|
+
l,
|
|
64
|
+
e.map((s) => s.color)
|
|
41
65
|
);
|
|
42
|
-
const
|
|
43
|
-
return
|
|
44
|
-
for (let
|
|
45
|
-
s
|
|
46
|
-
chart:
|
|
47
|
-
id:
|
|
48
|
-
data: e[
|
|
49
|
-
prev:
|
|
50
|
-
layerIndex:
|
|
66
|
+
const n = [];
|
|
67
|
+
return t.batch(() => {
|
|
68
|
+
for (let s = 0; s < e.length; s++)
|
|
69
|
+
n[s] = x({
|
|
70
|
+
chart: t,
|
|
71
|
+
id: l,
|
|
72
|
+
data: e[s].data,
|
|
73
|
+
prev: a.prev[s] ?? S,
|
|
74
|
+
layerIndex: s
|
|
51
75
|
});
|
|
52
|
-
}),
|
|
76
|
+
}), n;
|
|
53
77
|
}
|
|
54
|
-
function
|
|
55
|
-
const { chart:
|
|
78
|
+
function x(a) {
|
|
79
|
+
const { chart: t, id: l, data: e, prev: r, layerIndex: n } = a;
|
|
56
80
|
if (e.length === 0)
|
|
57
|
-
return
|
|
58
|
-
if (
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
81
|
+
return t.setSeriesData(l, [], n), { len: 0, firstTime: null, lastTime: null, data: [], last: null };
|
|
82
|
+
if (r.data === e && r.len === e.length)
|
|
83
|
+
return T(r, e) ? (t.updateData(l, e[e.length - 1], n), h(e)) : r;
|
|
84
|
+
const s = i(e[0]), u = i(e[e.length - 1]);
|
|
85
|
+
if (typeof s == "string")
|
|
86
|
+
return e.length === r.len && p(r.data, e) ? t.updateData(l, e[e.length - 1], n) : t.setSeriesData(l, e, n), h(e);
|
|
87
|
+
const f = r.firstTime !== null && r.firstTime !== s, c = e.length - r.len, D = r.lastTime !== null && r.lastTime !== u, g = f && c === 0 && D ? m(r, e, 1) : null;
|
|
88
|
+
if (g !== null)
|
|
89
|
+
t.batch(() => {
|
|
90
|
+
g === "lastEdited" && t.updateData(l, e[e.length - 2], n), t.appendData(l, e[e.length - 1], n), t.keepLast(l, e.length, n);
|
|
91
|
+
});
|
|
92
|
+
else if (E(r.data, e, c))
|
|
93
|
+
t.prependData(l, e.slice(0, c), n);
|
|
94
|
+
else if (r.len === 0 || e.length < r.len || c > b || f)
|
|
95
|
+
t.setSeriesData(l, e, n);
|
|
96
|
+
else if (e.length === r.len)
|
|
97
|
+
p(r.data, e) ? t.updateData(l, e[e.length - 1], n) : t.setSeriesData(l, e, n);
|
|
98
|
+
else {
|
|
99
|
+
const y = m(r, e, 0);
|
|
100
|
+
y === null ? t.setSeriesData(l, e, n) : t.batch(() => {
|
|
101
|
+
y === "lastEdited" && t.updateData(l, e[r.len - 1], n);
|
|
102
|
+
for (let d = r.len; d < e.length; d++)
|
|
103
|
+
t.appendData(l, e[d], n);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return h(e);
|
|
69
107
|
}
|
|
70
108
|
export {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
109
|
+
S as EMPTY_SYNC_STATE,
|
|
110
|
+
j as syncLayers,
|
|
111
|
+
x as syncSeriesLayer,
|
|
112
|
+
v as toLayers
|
|
75
113
|
};
|
|
@@ -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
|
|
@@ -20,7 +20,7 @@ function I(i) {
|
|
|
20
20
|
function O(i) {
|
|
21
21
|
return -I(i) / 12;
|
|
22
22
|
}
|
|
23
|
-
const
|
|
23
|
+
const V = "#9aa6b6", c = "#5d6877", C = "#e8eef7", x = "#cdd6e2", y = "#3fd68f", B = "#f5c451", $ = "#f4756a", Y = "#5d6877", X = {
|
|
24
24
|
position: "absolute",
|
|
25
25
|
top: "8px",
|
|
26
26
|
right: "8px",
|
|
@@ -34,7 +34,7 @@ const B = "#9aa6b6", c = "#5d6877", C = "#e8eef7", x = "#cdd6e2", y = "#3fd68f",
|
|
|
34
34
|
border: "1px solid rgba(255, 255, 255, 0.09)",
|
|
35
35
|
borderRadius: "10px",
|
|
36
36
|
boxShadow: "0 10px 28px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.05)",
|
|
37
|
-
color:
|
|
37
|
+
color: V,
|
|
38
38
|
font: "10px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace",
|
|
39
39
|
fontVariantNumeric: "tabular-nums",
|
|
40
40
|
letterSpacing: "0.02em",
|
|
@@ -50,7 +50,7 @@ function G(i) {
|
|
|
50
50
|
}`, i.head.appendChild(e);
|
|
51
51
|
}
|
|
52
52
|
function v(i) {
|
|
53
|
-
return i <= H ? y : i <= F ?
|
|
53
|
+
return i <= H ? y : i <= F ? B : $;
|
|
54
54
|
}
|
|
55
55
|
function A(i) {
|
|
56
56
|
return i >= 1e6 ? `${(i / 1e6).toFixed(1)}M` : i >= 1e4 ? `${(i / 1e3).toFixed(1)}k` : Math.round(i).toLocaleString("en-US");
|
|
@@ -74,8 +74,9 @@ class m {
|
|
|
74
74
|
const t = [];
|
|
75
75
|
let e = !1;
|
|
76
76
|
for (const [o, r] of this.#t) {
|
|
77
|
+
r.animating && (e = !0);
|
|
77
78
|
const i = r.current;
|
|
78
|
-
i <= 0 ||
|
|
79
|
+
i <= 0 || t.push({ value: o, opacity: i });
|
|
79
80
|
}
|
|
80
81
|
return { entries: t, isAnimating: e };
|
|
81
82
|
}
|
|
@@ -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
|
+
};
|