@wick-charts/vue 0.8.1 → 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/dist/BandSeries.vue.js +25 -24
- package/dist/BarSeries.vue.js +27 -26
- package/dist/CandlestickSeries.vue.js +25 -24
- package/dist/HeatmapSeries.vue.js +21 -20
- package/dist/LineSeries.vue.js +24 -23
- 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/options.js +1 -1
- package/dist/core/dist/chart/y-target.js +38 -32
- package/dist/core/dist/chart.js +475 -439
- 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 +80 -69
- package/dist/core/dist/perf/perf-hud.js +3 -3
- package/dist/core/dist/scales/tick-tracker.js +2 -1
- package/dist/core/dist/series/band.js +1 -1
- package/dist/core/dist/series/bar.js +240 -211
- package/dist/core/dist/series/base-multi-layer.js +267 -249
- package/dist/core/dist/series/candlestick.js +116 -116
- package/dist/core/dist/series/line.js +540 -455
- package/dist/core/dist/snapshots.js +96 -77
- package/dist/index.d.ts +22 -4
- package/dist/ui/InfoBar.vue.js +68 -68
- package/dist/ui/NumberFlow.vue.js +36 -27
- package/dist/ui/Tooltip.vue.js +127 -130
- package/package.json +2 -2
- package/src/BandSeries.vue +5 -3
- package/src/BarSeries.vue +5 -3
- package/src/CandlestickSeries.vue +5 -4
- package/src/ChartContainer.vue +1 -1
- package/src/HeatmapSeries.vue +5 -4
- package/src/LineSeries.vue +5 -3
- 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/dist/ui/Tooltip.vue.js
CHANGED
|
@@ -1,165 +1,162 @@
|
|
|
1
|
-
import { defineComponent as
|
|
2
|
-
import { buildHoverSnapshots as
|
|
3
|
-
import { resolveCandlestickBodyColor as
|
|
4
|
-
import { computeTooltipPosition as
|
|
5
|
-
import { formatCompact as
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
const ae = {
|
|
1
|
+
import { defineComponent as Y, computed as c, ref as z, onMounted as $, onUnmounted as K, useSlots as U, watchEffect as j, openBlock as r, createElementBlock as s, Fragment as x, unref as f, normalizeStyle as v, createElementVNode as u, toDisplayString as h, renderList as I, createCommentVNode as F, renderSlot as q } from "vue";
|
|
2
|
+
import { buildHoverSnapshots as G, formatTooltipHeader as J, formatRowValue as Q } from "../core/dist/snapshots.js";
|
|
3
|
+
import { resolveCandlestickBodyColor as Z } from "../core/dist/theme/resolve.js";
|
|
4
|
+
import { computeTooltipPosition as L } from "../core/dist/tooltip-position.js";
|
|
5
|
+
import { formatCompact as ee, formatPriceAdaptive as te } from "../core/dist/utils/format.js";
|
|
6
|
+
import { useCrosshairPosition as oe } from "../composables.js";
|
|
7
|
+
import { useChartInstance as ae } from "../context.js";
|
|
8
|
+
const le = {
|
|
10
9
|
key: 0,
|
|
11
10
|
style: { display: "grid", gridTemplateColumns: "auto 1fr", gap: "4px 12px" }
|
|
12
|
-
},
|
|
11
|
+
}, ie = { style: { opacity: 0.5 } }, ne = {
|
|
13
12
|
key: 1,
|
|
14
13
|
style: { display: "flex", alignItems: "center", gap: "8px", padding: "2px 0" }
|
|
15
|
-
},
|
|
14
|
+
}, re = { style: {
|
|
16
15
|
opacity: 0.6,
|
|
17
16
|
flex: "1",
|
|
18
17
|
minWidth: 0,
|
|
19
18
|
overflow: "hidden",
|
|
20
19
|
textOverflow: "ellipsis",
|
|
21
20
|
whiteSpace: "nowrap"
|
|
22
|
-
} },
|
|
21
|
+
} }, se = {
|
|
23
22
|
key: 2,
|
|
24
23
|
style: { display: "flex", alignItems: "center", gap: "8px", padding: "2px 0" }
|
|
25
|
-
},
|
|
24
|
+
}, ue = { style: { opacity: 0.6, flex: "1" } }, pe = ["data-measured"], ge = /* @__PURE__ */ Y({
|
|
26
25
|
__name: "Tooltip",
|
|
27
26
|
props: {
|
|
28
27
|
sort: {},
|
|
29
28
|
format: { type: Function }
|
|
30
29
|
},
|
|
31
|
-
setup(
|
|
32
|
-
const
|
|
33
|
-
() =>
|
|
34
|
-
),
|
|
35
|
-
|
|
30
|
+
setup(V) {
|
|
31
|
+
const W = V, O = c(() => W.sort ?? "none"), g = c(
|
|
32
|
+
() => W.format ?? ((l, n) => n === "volume" ? ee(l) : te(l))
|
|
33
|
+
), o = ae(), p = oe(o), y = z(0), w = () => {
|
|
34
|
+
y.value++;
|
|
36
35
|
};
|
|
37
36
|
$(() => {
|
|
38
|
-
|
|
39
|
-
}),
|
|
40
|
-
|
|
37
|
+
o.on("overlayChange", w), o.on("seriesFrame", w), o.getSeriesIds().length > 0 && y.value++;
|
|
38
|
+
}), K(() => {
|
|
39
|
+
o.off("overlayChange", w), o.off("seriesFrame", w);
|
|
41
40
|
});
|
|
42
|
-
const
|
|
43
|
-
time:
|
|
41
|
+
const t = c(() => (y.value, o.getTheme())), T = c(() => (y.value, o.getDataInterval())), d = c(() => (y.value, p.value ? G(o, {
|
|
42
|
+
time: p.value.time,
|
|
44
43
|
sort: O.value,
|
|
45
44
|
cacheKey: "tooltip"
|
|
46
|
-
}) : [])),
|
|
47
|
-
let
|
|
48
|
-
|
|
49
|
-
if (!
|
|
50
|
-
const
|
|
51
|
-
!
|
|
52
|
-
const
|
|
53
|
-
if (!
|
|
54
|
-
const
|
|
55
|
-
(!
|
|
56
|
-
}),
|
|
57
|
-
|
|
45
|
+
}) : [])), R = c(() => p.value?.time ?? 0), A = U(), b = c(() => typeof A.default == "function"), B = z(null), k = z(null);
|
|
46
|
+
let C = null;
|
|
47
|
+
j((l) => {
|
|
48
|
+
if (!b.value) return;
|
|
49
|
+
const n = B.value;
|
|
50
|
+
!n || typeof ResizeObserver > "u" || (C = new ResizeObserver((e) => {
|
|
51
|
+
const a = e[0]?.contentRect;
|
|
52
|
+
if (!a) return;
|
|
53
|
+
const S = k.value;
|
|
54
|
+
(!S || S.width !== a.width || S.height !== a.height) && (k.value = { width: a.width, height: a.height });
|
|
55
|
+
}), C.observe(n), l(() => {
|
|
56
|
+
C?.disconnect(), C = null;
|
|
58
57
|
}));
|
|
59
58
|
});
|
|
60
|
-
const
|
|
61
|
-
if (!
|
|
62
|
-
const
|
|
63
|
-
if (
|
|
64
|
-
const
|
|
65
|
-
return
|
|
66
|
-
x:
|
|
67
|
-
y:
|
|
68
|
-
chartLeft:
|
|
59
|
+
const m = c(() => {
|
|
60
|
+
if (!p.value || d.value.length === 0) return null;
|
|
61
|
+
const l = o.getMediaSize(), n = o.plotLeft, e = o.getLayout().chartArea.width, a = l.height - o.xAxisHeight;
|
|
62
|
+
if (b.value) {
|
|
63
|
+
const i = k.value;
|
|
64
|
+
return i ? L({
|
|
65
|
+
x: p.value.mediaX,
|
|
66
|
+
y: p.value.mediaY,
|
|
67
|
+
chartLeft: n,
|
|
69
68
|
chartWidth: e,
|
|
70
|
-
chartHeight:
|
|
71
|
-
tooltipWidth:
|
|
72
|
-
tooltipHeight:
|
|
73
|
-
}) : { left:
|
|
69
|
+
chartHeight: a,
|
|
70
|
+
tooltipWidth: i.width,
|
|
71
|
+
tooltipHeight: i.height
|
|
72
|
+
}) : { left: n, top: 0 };
|
|
74
73
|
}
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
74
|
+
const S = d.value.some((i) => "open" in i.data), P = d.value.filter((i) => !("open" in i.data)).length, M = d.value.some((i) => "high" in i.data && !("open" in i.data)), D = d.value.some((i) => i.share !== void 0);
|
|
75
|
+
let _ = 160;
|
|
76
|
+
M && (_ = 220), D && (_ = 240);
|
|
77
|
+
const X = S ? 140 : 40 + P * 22;
|
|
78
|
+
return L({
|
|
79
|
+
x: p.value.mediaX,
|
|
80
|
+
y: p.value.mediaY,
|
|
81
|
+
chartLeft: n,
|
|
80
82
|
chartWidth: e,
|
|
81
|
-
chartHeight:
|
|
82
|
-
tooltipWidth:
|
|
83
|
-
tooltipHeight:
|
|
83
|
+
chartHeight: a,
|
|
84
|
+
tooltipWidth: _,
|
|
85
|
+
tooltipHeight: X
|
|
84
86
|
});
|
|
85
|
-
}), E =
|
|
86
|
-
function
|
|
87
|
-
return "open" in
|
|
87
|
+
}), E = c(() => !b.value || k.value !== null);
|
|
88
|
+
function H(l) {
|
|
89
|
+
return "open" in l;
|
|
88
90
|
}
|
|
89
|
-
function N(
|
|
90
|
-
return "high" in
|
|
91
|
+
function N(l) {
|
|
92
|
+
return "high" in l && !H(l);
|
|
91
93
|
}
|
|
92
|
-
return (
|
|
93
|
-
|
|
94
|
+
return (l, n) => (r(), s(x, null, [
|
|
95
|
+
f(p) && d.value.length > 0 && m.value && !b.value ? (r(), s("div", {
|
|
94
96
|
key: 0,
|
|
95
|
-
style:
|
|
97
|
+
style: v({
|
|
96
98
|
position: "absolute",
|
|
97
|
-
left:
|
|
98
|
-
top:
|
|
99
|
+
left: m.value.left + "px",
|
|
100
|
+
top: m.value.top + "px",
|
|
99
101
|
pointerEvents: "none",
|
|
100
|
-
background:
|
|
102
|
+
background: t.value.tooltip.background,
|
|
101
103
|
backdropFilter: "blur(12px)",
|
|
102
104
|
WebkitBackdropFilter: "blur(12px)",
|
|
103
|
-
border: "1px solid " +
|
|
105
|
+
border: "1px solid " + t.value.tooltip.borderColor,
|
|
104
106
|
borderRadius: "8px",
|
|
105
107
|
padding: "10px 14px",
|
|
106
108
|
boxShadow: "0 4px 16px rgba(0,0,0,0.1), 0 1px 4px rgba(0,0,0,0.06)",
|
|
107
|
-
fontSize:
|
|
108
|
-
fontFamily:
|
|
109
|
+
fontSize: t.value.tooltip.fontSize + "px",
|
|
110
|
+
fontFamily: t.value.typography.fontFamily,
|
|
109
111
|
fontVariantNumeric: "tabular-nums",
|
|
110
|
-
color:
|
|
112
|
+
color: t.value.tooltip.textColor,
|
|
111
113
|
minWidth: "140px",
|
|
112
114
|
zIndex: 10,
|
|
113
115
|
transition: "opacity 0.15s ease"
|
|
114
116
|
})
|
|
115
117
|
}, [
|
|
116
|
-
|
|
117
|
-
style:
|
|
118
|
-
fontSize:
|
|
119
|
-
color:
|
|
118
|
+
u("div", {
|
|
119
|
+
style: v({
|
|
120
|
+
fontSize: t.value.axis.fontSize + "px",
|
|
121
|
+
color: t.value.axis.textColor,
|
|
120
122
|
marginBottom: "8px",
|
|
121
123
|
paddingBottom: "6px",
|
|
122
|
-
borderBottom: "1px solid " +
|
|
124
|
+
borderBottom: "1px solid " + t.value.tooltip.borderColor,
|
|
123
125
|
letterSpacing: "0.02em"
|
|
124
126
|
})
|
|
125
|
-
},
|
|
126
|
-
|
|
127
|
-
H(c(d(ee)(_.value, { locale: d(t).timeScale.getLocale(), timeZone: d(t).timeScale.getTimeZone() })), 1)
|
|
128
|
-
], 64)) : w("", !0),
|
|
129
|
-
H(" " + c(d(q)(d(t), { time: _.value, snapshots: v.value, interval: V.value })), 1)
|
|
130
|
-
], 4),
|
|
131
|
-
(i(!0), n(f, null, L(v.value, (e) => (i(), n(f, {
|
|
127
|
+
}, h(f(J)(f(o), { time: R.value, snapshots: d.value, interval: T.value })), 5),
|
|
128
|
+
(r(!0), s(x, null, I(d.value, (e) => (r(), s(x, {
|
|
132
129
|
key: e.id
|
|
133
130
|
}, [
|
|
134
|
-
|
|
135
|
-
(
|
|
131
|
+
H(e.data) ? (r(), s("div", le, [
|
|
132
|
+
(r(!0), s(x, null, I([
|
|
136
133
|
{ label: "Open", val: e.data.open, field: "open" },
|
|
137
134
|
{ label: "High", val: e.data.high, field: "high" },
|
|
138
135
|
{ label: "Low", val: e.data.low, field: "low" },
|
|
139
136
|
{ label: "Close", val: e.data.close, field: "close" }
|
|
140
|
-
], (
|
|
141
|
-
key:
|
|
137
|
+
], (a) => (r(), s(x, {
|
|
138
|
+
key: a.label
|
|
142
139
|
}, [
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
style:
|
|
140
|
+
u("span", ie, h(a.label), 1),
|
|
141
|
+
u("span", {
|
|
142
|
+
style: v({
|
|
146
143
|
fontWeight: 600,
|
|
147
|
-
color:
|
|
148
|
-
e.data.close >= e.data.open ?
|
|
144
|
+
color: f(Z)(
|
|
145
|
+
e.data.close >= e.data.open ? t.value.candlestick.up.body : t.value.candlestick.down.body
|
|
149
146
|
),
|
|
150
147
|
textAlign: "right"
|
|
151
148
|
})
|
|
152
|
-
},
|
|
149
|
+
}, h(g.value(a.val, a.field)), 5)
|
|
153
150
|
], 64))), 128)),
|
|
154
|
-
e.data.volume != null ? (
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
style:
|
|
158
|
-
},
|
|
159
|
-
], 64)) :
|
|
160
|
-
])) : N(e.data) ? (
|
|
161
|
-
|
|
162
|
-
style:
|
|
151
|
+
e.data.volume != null ? (r(), s(x, { key: 0 }, [
|
|
152
|
+
n[0] || (n[0] = u("span", { style: { opacity: 0.5 } }, "Volume", -1)),
|
|
153
|
+
u("span", {
|
|
154
|
+
style: v({ fontWeight: 600, color: t.value.tooltip.textColor, textAlign: "right" })
|
|
155
|
+
}, h(g.value(e.data.volume, "volume")), 5)
|
|
156
|
+
], 64)) : F("", !0)
|
|
157
|
+
])) : N(e.data) ? (r(), s("div", ne, [
|
|
158
|
+
u("span", {
|
|
159
|
+
style: v({
|
|
163
160
|
width: "8px",
|
|
164
161
|
height: "8px",
|
|
165
162
|
borderRadius: "2px",
|
|
@@ -167,13 +164,13 @@ const ae = {
|
|
|
167
164
|
flexShrink: 0
|
|
168
165
|
})
|
|
169
166
|
}, null, 4),
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
style:
|
|
173
|
-
},
|
|
174
|
-
])) : (
|
|
175
|
-
|
|
176
|
-
style:
|
|
167
|
+
u("span", re, h(e.label ?? "Range"), 1),
|
|
168
|
+
u("span", {
|
|
169
|
+
style: v({ fontWeight: 600, color: e.color, whiteSpace: "nowrap" })
|
|
170
|
+
}, h(g.value(e.data.low, "low")) + " – " + h(g.value(e.data.high, "high")), 5)
|
|
171
|
+
])) : (r(), s("div", se, [
|
|
172
|
+
u("span", {
|
|
173
|
+
style: v({
|
|
177
174
|
width: "8px",
|
|
178
175
|
height: "8px",
|
|
179
176
|
borderRadius: "50%",
|
|
@@ -181,49 +178,49 @@ const ae = {
|
|
|
181
178
|
flexShrink: 0
|
|
182
179
|
})
|
|
183
180
|
}, null, 4),
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
style:
|
|
187
|
-
},
|
|
181
|
+
u("span", ue, h(e.label ?? "Value"), 1),
|
|
182
|
+
u("span", {
|
|
183
|
+
style: v({ fontWeight: 600, color: e.color })
|
|
184
|
+
}, h(f(Q)(e, (a) => g.value(a, "value"))), 5)
|
|
188
185
|
]))
|
|
189
186
|
], 64))), 128))
|
|
190
|
-
], 4)) :
|
|
191
|
-
|
|
187
|
+
], 4)) : F("", !0),
|
|
188
|
+
f(p) && d.value.length > 0 && m.value && b.value ? (r(), s("div", {
|
|
192
189
|
key: 1,
|
|
193
190
|
ref_key: "containerRef",
|
|
194
191
|
ref: B,
|
|
195
192
|
"data-measured": E.value ? "true" : "false",
|
|
196
|
-
style:
|
|
193
|
+
style: v({
|
|
197
194
|
position: "absolute",
|
|
198
|
-
left:
|
|
199
|
-
top:
|
|
195
|
+
left: m.value.left + "px",
|
|
196
|
+
top: m.value.top + "px",
|
|
200
197
|
pointerEvents: "none",
|
|
201
|
-
background:
|
|
198
|
+
background: t.value.tooltip.background,
|
|
202
199
|
backdropFilter: "blur(12px)",
|
|
203
200
|
WebkitBackdropFilter: "blur(12px)",
|
|
204
|
-
border: "1px solid " +
|
|
201
|
+
border: "1px solid " + t.value.tooltip.borderColor,
|
|
205
202
|
borderRadius: "8px",
|
|
206
203
|
padding: "10px 14px",
|
|
207
204
|
boxShadow: "0 4px 16px rgba(0,0,0,0.1), 0 1px 4px rgba(0,0,0,0.06)",
|
|
208
|
-
fontFamily:
|
|
209
|
-
fontSize:
|
|
205
|
+
fontFamily: t.value.typography.fontFamily,
|
|
206
|
+
fontSize: t.value.tooltip.fontSize + "px",
|
|
210
207
|
fontVariantNumeric: "tabular-nums",
|
|
211
|
-
color:
|
|
208
|
+
color: t.value.tooltip.textColor,
|
|
212
209
|
width: "max-content",
|
|
213
|
-
maxWidth:
|
|
210
|
+
maxWidth: f(o).getLayout().chartArea.width + "px",
|
|
214
211
|
boxSizing: "border-box",
|
|
215
212
|
zIndex: 10,
|
|
216
|
-
visibility:
|
|
213
|
+
visibility: k.value !== null ? "visible" : "hidden"
|
|
217
214
|
})
|
|
218
215
|
}, [
|
|
219
|
-
|
|
220
|
-
snapshots:
|
|
221
|
-
time:
|
|
216
|
+
q(l.$slots, "default", {
|
|
217
|
+
snapshots: d.value,
|
|
218
|
+
time: R.value
|
|
222
219
|
})
|
|
223
|
-
], 12,
|
|
220
|
+
], 12, pe)) : F("", !0)
|
|
224
221
|
], 64));
|
|
225
222
|
}
|
|
226
223
|
});
|
|
227
224
|
export {
|
|
228
|
-
|
|
225
|
+
ge as default
|
|
229
226
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wick-charts/vue",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "Realtime, animated canvas charts for Vue: candlestick, line, bar, heatmap, pie and more. Tree-shakeable, zero runtime deps.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"vue": ">=3.3.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@wick-charts/core": "^0.8.
|
|
54
|
+
"@wick-charts/core": "^0.8.2"
|
|
55
55
|
},
|
|
56
56
|
"scripts": {
|
|
57
57
|
"build": "vite build"
|
package/src/BandSeries.vue
CHANGED
|
@@ -3,6 +3,7 @@ import type { BandData, BandSeriesOptions, SeriesSyncState } from '@wick-charts/
|
|
|
3
3
|
import { BandSeriesDef, EMPTY_SYNC_STATE, syncLayers, toLayers } from '@wick-charts/core';
|
|
4
4
|
import { onMounted, onUnmounted, ref, watch } from 'vue';
|
|
5
5
|
|
|
6
|
+
import { rawSeriesData, seriesDataSource } from './composables';
|
|
6
7
|
import { useChartInstance } from './context';
|
|
7
8
|
|
|
8
9
|
const props = withDefaults(
|
|
@@ -58,7 +59,7 @@ function createSeries(data: BandData): void {
|
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
onMounted(() => {
|
|
61
|
-
createSeries(props.data);
|
|
62
|
+
createSeries(rawSeriesData(props.data));
|
|
62
63
|
});
|
|
63
64
|
|
|
64
65
|
onUnmounted(() => {
|
|
@@ -66,8 +67,9 @@ onUnmounted(() => {
|
|
|
66
67
|
});
|
|
67
68
|
|
|
68
69
|
watch(
|
|
69
|
-
() => props.data,
|
|
70
|
-
(
|
|
70
|
+
seriesDataSource(() => props.data),
|
|
71
|
+
() => {
|
|
72
|
+
const data = rawSeriesData(props.data);
|
|
71
73
|
const id = seriesId.value;
|
|
72
74
|
if (!id) return;
|
|
73
75
|
|
package/src/BarSeries.vue
CHANGED
|
@@ -3,6 +3,7 @@ import type { BarSeriesOptions, MultiLayerData, SeriesSyncState } from '@wick-ch
|
|
|
3
3
|
import { BarSeriesDef, EMPTY_SYNC_STATE, syncLayers, toLayers } from '@wick-charts/core';
|
|
4
4
|
import { onMounted, onUnmounted, ref, watch } from 'vue';
|
|
5
5
|
|
|
6
|
+
import { rawSeriesData, seriesDataSource } from './composables';
|
|
6
7
|
import { useChartInstance } from './context';
|
|
7
8
|
|
|
8
9
|
const props = withDefaults(
|
|
@@ -54,7 +55,7 @@ function createSeries(data: MultiLayerData): void {
|
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
onMounted(() => {
|
|
57
|
-
createSeries(props.data);
|
|
58
|
+
createSeries(rawSeriesData(props.data));
|
|
58
59
|
});
|
|
59
60
|
|
|
60
61
|
onUnmounted(() => {
|
|
@@ -62,8 +63,9 @@ onUnmounted(() => {
|
|
|
62
63
|
});
|
|
63
64
|
|
|
64
65
|
watch(
|
|
65
|
-
() => props.data,
|
|
66
|
-
(
|
|
66
|
+
seriesDataSource(() => props.data),
|
|
67
|
+
() => {
|
|
68
|
+
const data = rawSeriesData(props.data);
|
|
67
69
|
const id = seriesId.value;
|
|
68
70
|
if (!id) return;
|
|
69
71
|
|
|
@@ -3,6 +3,7 @@ import type { CandlestickData, CandlestickSeriesOptions, OHLCInput, SeriesSyncSt
|
|
|
3
3
|
import { CandlestickSeriesDef, EMPTY_SYNC_STATE, syncSeriesLayer, toLayers } from '@wick-charts/core';
|
|
4
4
|
import { onMounted, onUnmounted, ref, watch } from 'vue';
|
|
5
5
|
|
|
6
|
+
import { rawSeriesData, seriesDataSource } from './composables';
|
|
6
7
|
import { useChartInstance } from './context';
|
|
7
8
|
|
|
8
9
|
const props = withDefaults(
|
|
@@ -47,7 +48,7 @@ onMounted(() => {
|
|
|
47
48
|
// Initial data load — Vue's `watch` is lazy by default, so the watcher
|
|
48
49
|
// below only fires on subsequent `data` prop mutations. Explicitly apply
|
|
49
50
|
// the first value here so components with static data render immediately.
|
|
50
|
-
applyData(id, props.data);
|
|
51
|
+
applyData(id, rawSeriesData(props.data));
|
|
51
52
|
chart.setSeriesVisible(id, props.visible);
|
|
52
53
|
});
|
|
53
54
|
|
|
@@ -56,12 +57,12 @@ onUnmounted(() => {
|
|
|
56
57
|
});
|
|
57
58
|
|
|
58
59
|
watch(
|
|
59
|
-
() => props.data,
|
|
60
|
-
(
|
|
60
|
+
seriesDataSource(() => props.data),
|
|
61
|
+
() => {
|
|
61
62
|
const id = seriesId.value;
|
|
62
63
|
if (!id) return;
|
|
63
64
|
|
|
64
|
-
applyData(id, data);
|
|
65
|
+
applyData(id, rawSeriesData(props.data));
|
|
65
66
|
},
|
|
66
67
|
);
|
|
67
68
|
|
package/src/ChartContainer.vue
CHANGED
|
@@ -48,7 +48,7 @@ const props = withDefaults(
|
|
|
48
48
|
* Viewport padding. `top`/`bottom` are in pixels. `left`/`right` accept either pixels (`50`)
|
|
49
49
|
* or data intervals (`{ intervals: 3 }`). Set to 0 for edge-to-edge sparklines. Updates are
|
|
50
50
|
* applied reactively when this prop or the measured header height changes.
|
|
51
|
-
* Defaults: `{ top: 20, bottom: 20, right: { intervals: 3 }, left: { intervals: 0 } }`.
|
|
51
|
+
* Defaults: `{ top: 20, bottom: 20, right: { intervals: 3 }, left: { intervals: 0.5 } }`.
|
|
52
52
|
*/
|
|
53
53
|
padding?: {
|
|
54
54
|
top?: number;
|
package/src/HeatmapSeries.vue
CHANGED
|
@@ -3,6 +3,7 @@ import type { HeatmapCellData, HeatmapSeriesOptions } from '@wick-charts/core';
|
|
|
3
3
|
import { HeatmapSeriesDef } from '@wick-charts/core';
|
|
4
4
|
import { onMounted, onUnmounted, ref, watch } from 'vue';
|
|
5
5
|
|
|
6
|
+
import { rawSeriesData, seriesDataSource } from './composables';
|
|
6
7
|
import { useChartInstance } from './context';
|
|
7
8
|
|
|
8
9
|
const props = withDefaults(
|
|
@@ -28,7 +29,7 @@ onMounted(() => {
|
|
|
28
29
|
seriesId.value = id;
|
|
29
30
|
// Lazy watcher — apply initial data here so static-data mounts render without a no-op first frame.
|
|
30
31
|
if (props.data.length > 0) {
|
|
31
|
-
chart.setSeriesData(id, props.data);
|
|
32
|
+
chart.setSeriesData(id, rawSeriesData(props.data));
|
|
32
33
|
}
|
|
33
34
|
chart.setSeriesVisible(id, props.visible);
|
|
34
35
|
});
|
|
@@ -38,12 +39,12 @@ onUnmounted(() => {
|
|
|
38
39
|
});
|
|
39
40
|
|
|
40
41
|
watch(
|
|
41
|
-
() => props.data,
|
|
42
|
-
(
|
|
42
|
+
seriesDataSource(() => props.data),
|
|
43
|
+
() => {
|
|
43
44
|
// No length guard: an empty array is a legitimate update (filter yields
|
|
44
45
|
// no rows) and must clear the previous cells, matching the React wrapper.
|
|
45
46
|
if (seriesId.value) {
|
|
46
|
-
chart.setSeriesData(seriesId.value, data);
|
|
47
|
+
chart.setSeriesData(seriesId.value, rawSeriesData(props.data));
|
|
47
48
|
}
|
|
48
49
|
},
|
|
49
50
|
);
|
package/src/LineSeries.vue
CHANGED
|
@@ -3,6 +3,7 @@ import type { LineSeriesOptions, MultiLayerData, SeriesSyncState } from '@wick-c
|
|
|
3
3
|
import { EMPTY_SYNC_STATE, LineSeriesDef, syncLayers, toLayers } from '@wick-charts/core';
|
|
4
4
|
import { onMounted, onUnmounted, ref, watch } from 'vue';
|
|
5
5
|
|
|
6
|
+
import { rawSeriesData, seriesDataSource } from './composables';
|
|
6
7
|
import { useChartInstance } from './context';
|
|
7
8
|
|
|
8
9
|
const props = withDefaults(
|
|
@@ -54,7 +55,7 @@ function createSeries(data: MultiLayerData): void {
|
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
onMounted(() => {
|
|
57
|
-
createSeries(props.data);
|
|
58
|
+
createSeries(rawSeriesData(props.data));
|
|
58
59
|
});
|
|
59
60
|
|
|
60
61
|
onUnmounted(() => {
|
|
@@ -62,8 +63,9 @@ onUnmounted(() => {
|
|
|
62
63
|
});
|
|
63
64
|
|
|
64
65
|
watch(
|
|
65
|
-
() => props.data,
|
|
66
|
-
(
|
|
66
|
+
seriesDataSource(() => props.data),
|
|
67
|
+
() => {
|
|
68
|
+
const data = rawSeriesData(props.data);
|
|
67
69
|
const id = seriesId.value;
|
|
68
70
|
if (!id) return;
|
|
69
71
|
|
package/src/PieSeries.vue
CHANGED
|
@@ -3,6 +3,7 @@ import type { PieSeriesOptions, PieSliceData } from '@wick-charts/core';
|
|
|
3
3
|
import { PieSeriesDef } from '@wick-charts/core';
|
|
4
4
|
import { onMounted, onUnmounted, ref, watch } from 'vue';
|
|
5
5
|
|
|
6
|
+
import { rawSeriesData, seriesDataSource } from './composables';
|
|
6
7
|
import { useChartInstance } from './context';
|
|
7
8
|
|
|
8
9
|
const props = withDefaults(
|
|
@@ -28,7 +29,7 @@ onMounted(() => {
|
|
|
28
29
|
seriesId.value = id;
|
|
29
30
|
// Lazy watcher — apply initial data here so static-data mounts render without a no-op first frame.
|
|
30
31
|
if (props.data.length > 0) {
|
|
31
|
-
chart.setSeriesData(id, props.data);
|
|
32
|
+
chart.setSeriesData(id, rawSeriesData(props.data));
|
|
32
33
|
}
|
|
33
34
|
chart.setSeriesVisible(id, props.visible);
|
|
34
35
|
});
|
|
@@ -38,10 +39,10 @@ onUnmounted(() => {
|
|
|
38
39
|
});
|
|
39
40
|
|
|
40
41
|
watch(
|
|
41
|
-
() => props.data,
|
|
42
|
-
(
|
|
43
|
-
if (seriesId.value && data.length > 0) {
|
|
44
|
-
chart.setSeriesData(seriesId.value, data);
|
|
42
|
+
seriesDataSource(() => props.data),
|
|
43
|
+
() => {
|
|
44
|
+
if (seriesId.value && props.data.length > 0) {
|
|
45
|
+
chart.setSeriesData(seriesId.value, rawSeriesData(props.data));
|
|
45
46
|
}
|
|
46
47
|
},
|
|
47
48
|
);
|
package/src/composables.ts
CHANGED
|
@@ -1,5 +1,68 @@
|
|
|
1
1
|
import type { ChartInstance, CrosshairPosition, VisibleRange, YAxisPosition, YRange } from '@wick-charts/core';
|
|
2
|
-
import { type Ref, onMounted, onUnmounted, ref } from 'vue';
|
|
2
|
+
import { type Ref, onMounted, onUnmounted, ref, toRaw } from 'vue';
|
|
3
|
+
|
|
4
|
+
/** A `{ label?, color?, data }` layer — an object carrying a `data` array. */
|
|
5
|
+
function isLayerObject(value: unknown): value is { data: unknown[] } {
|
|
6
|
+
return (
|
|
7
|
+
typeof value === 'object' && value !== null && !Array.isArray(value) && Array.isArray(Reflect.get(value, 'data'))
|
|
8
|
+
);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Each layer's point array, read through whatever Proxy the prop arrived in. */
|
|
12
|
+
function layerArrays(data: unknown): unknown[][] {
|
|
13
|
+
if (isLayerObject(data)) return [data.data];
|
|
14
|
+
if (!Array.isArray(data)) return [];
|
|
15
|
+
|
|
16
|
+
const layered = Array.isArray(data[0]) || isLayerObject(data[0]);
|
|
17
|
+
if (!layered) return [data];
|
|
18
|
+
|
|
19
|
+
return data.map((layer) => (isLayerObject(layer) ? layer.data : (layer as unknown[])));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* A series `data` prop with Vue's reactive Proxies taken off the top level and
|
|
24
|
+
* each layer, so the chart reads plain arrays: a `ref` / `reactive` array of
|
|
25
|
+
* 100k points read through its Proxy costs the engine several times more.
|
|
26
|
+
*/
|
|
27
|
+
export function rawSeriesData<D>(data: D): D {
|
|
28
|
+
const raw = toRaw(data);
|
|
29
|
+
const rawLayer = (layer: unknown) => {
|
|
30
|
+
if (!isLayerObject(layer)) return toRaw(layer);
|
|
31
|
+
|
|
32
|
+
const plain = toRaw(layer);
|
|
33
|
+
|
|
34
|
+
return { ...plain, data: toRaw(plain.data) };
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
if (!Array.isArray(raw)) return rawLayer(raw) as D;
|
|
38
|
+
|
|
39
|
+
const layered = Array.isArray(raw[0]) || isLayerObject(raw[0]);
|
|
40
|
+
if (!layered) return raw;
|
|
41
|
+
|
|
42
|
+
return raw.map(rawLayer) as D;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Watch source for a series `data` prop. Besides a new array, it re-fires on
|
|
47
|
+
* an in-place `push` / `splice` or an edit of a layer's last point on a
|
|
48
|
+
* reactive array — it reads each layer's length and last point through the
|
|
49
|
+
* Proxy instead of deep-watching every point. A `shallowRef` still needs a
|
|
50
|
+
* new array (or `triggerRef`).
|
|
51
|
+
*/
|
|
52
|
+
export function seriesDataSource(getData: () => unknown): () => unknown[] {
|
|
53
|
+
return () => {
|
|
54
|
+
const data = getData();
|
|
55
|
+
const deps: unknown[] = [data];
|
|
56
|
+
for (const points of layerArrays(data)) {
|
|
57
|
+
deps.push(points, points.length);
|
|
58
|
+
|
|
59
|
+
const last = points[points.length - 1];
|
|
60
|
+
if (typeof last === 'object' && last !== null) deps.push(...Object.values(last));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return deps;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
3
66
|
|
|
4
67
|
export function useVisibleRange(chart: ChartInstance): Ref<VisibleRange> {
|
|
5
68
|
const range = ref<VisibleRange>(chart.getVisibleRange()) as Ref<VisibleRange>;
|