@wick-charts/vue 0.4.1 → 0.5.0
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 +5 -3
- package/dist/BarSeries.vue.js +44 -0
- package/dist/BarSeries.vue2.js +4 -0
- package/dist/CandlestickSeries.vue.js +45 -0
- package/dist/CandlestickSeries.vue2.js +4 -0
- package/dist/ChartContainer.vue.js +168 -0
- package/dist/ChartContainer.vue2.js +4 -0
- package/dist/LineSeries.vue.js +44 -0
- package/dist/LineSeries.vue2.js +4 -0
- package/dist/PieSeries.vue.js +34 -0
- package/dist/PieSeries.vue2.js +4 -0
- package/dist/composables.js +44 -0
- package/dist/context.js +25 -0
- package/dist/core/dist/animation/animator.js +72 -0
- package/dist/core/dist/animation/config.js +145 -0
- package/dist/core/dist/animation/easing.js +4 -0
- package/dist/core/dist/animation/scalar-spring.js +57 -0
- package/dist/core/dist/animation/spring.js +11 -0
- package/dist/core/dist/animation/time.js +19 -0
- package/dist/core/dist/animation/viewport-engine.js +174 -0
- package/dist/core/dist/animation/visible-range-spring.js +64 -0
- package/dist/core/dist/animation/y-range-hermite.js +72 -0
- package/dist/core/dist/animation/y-range-snap.js +31 -0
- package/dist/core/dist/animation/y-range-spring.js +59 -0
- package/dist/core/dist/axis/dom-labels.js +60 -0
- package/dist/core/dist/canvas-manager.js +109 -0
- package/dist/core/dist/chart/edge-indicators.js +26 -0
- package/dist/core/dist/chart/fit-to-data.js +9 -0
- package/dist/core/dist/chart/last-value.js +31 -0
- package/dist/core/dist/chart/options.js +35 -0
- package/dist/core/dist/chart/pan-zoom-math.js +66 -0
- package/dist/core/dist/chart/streaming-cadence.js +70 -0
- package/dist/core/dist/chart/streaming-target.js +22 -0
- package/dist/core/dist/chart/viewport-padding.js +6 -0
- package/dist/core/dist/chart/y-target.js +24 -0
- package/dist/core/dist/chart.js +1495 -0
- package/dist/core/dist/components/crosshair.js +9 -0
- package/dist/core/dist/components/edge-indicator.js +36 -0
- package/dist/core/dist/components/grid.js +22 -0
- package/dist/core/dist/components/marker.js +41 -0
- package/dist/core/dist/components/reference-line.js +48 -0
- package/dist/core/dist/components/time-region.js +35 -0
- package/dist/core/dist/data/decimation.js +54 -0
- package/dist/core/dist/data/store.js +76 -0
- package/dist/core/dist/data/sync.js +47 -0
- package/dist/core/dist/events.js +19 -0
- package/dist/core/dist/interactions/handler.js +84 -0
- package/dist/core/dist/interactions/pan.js +26 -0
- package/dist/core/dist/interactions/zoom.js +21 -0
- package/dist/core/dist/navigator/controller.js +374 -0
- package/dist/core/dist/navigator/decimate.js +44 -0
- package/dist/core/dist/navigator/interactions.js +28 -0
- package/dist/core/dist/navigator/render.js +68 -0
- package/dist/core/dist/perf/counting-context.js +19 -0
- package/dist/core/dist/perf/perf-hud.js +453 -0
- package/dist/core/dist/perf/perf-monitor.js +132 -0
- package/dist/core/dist/render-scheduler.js +31 -0
- package/dist/core/dist/scales/tick-tracker.js +110 -0
- package/dist/core/dist/scales/x-scale.js +145 -0
- package/dist/core/dist/scales/y-scale.js +156 -0
- package/dist/core/dist/series/bar.js +341 -0
- package/dist/core/dist/series/base-multi-layer.js +438 -0
- package/dist/core/dist/series/candlestick.js +481 -0
- package/dist/core/dist/series/definition.js +11 -0
- package/dist/core/dist/series/line.js +446 -0
- package/dist/core/dist/series/painters/builtins.js +36 -0
- package/dist/core/dist/series/painters/canvas-path.js +79 -0
- package/dist/core/dist/series/painters/resolve.js +32 -0
- package/dist/core/dist/series/pie.js +545 -0
- package/dist/core/dist/series/register-builtins.js +11 -0
- package/dist/core/dist/series/stack-math.js +17 -0
- package/dist/core/dist/snapshots.js +107 -0
- package/dist/core/dist/theme/create.js +176 -0
- package/dist/core/dist/theme/resolve.js +14 -0
- package/dist/core/dist/theme/themes/andromeda.js +33 -0
- package/dist/core/dist/theme/themes/ayu-mirage.js +33 -0
- package/dist/core/dist/theme/themes/catppuccin.js +33 -0
- package/dist/core/dist/theme/themes/dracula.js +35 -0
- package/dist/core/dist/theme/themes/github-light.js +34 -0
- package/dist/core/dist/theme/themes/gruvbox.js +33 -0
- package/dist/core/dist/theme/themes/handwritten.js +101 -0
- package/dist/core/dist/theme/themes/high-contrast.js +33 -0
- package/dist/core/dist/theme/themes/lavender-mist.js +33 -0
- package/dist/core/dist/theme/themes/light-pink.js +34 -0
- package/dist/core/dist/theme/themes/material-palenight.js +33 -0
- package/dist/core/dist/theme/themes/minimal-light.js +34 -0
- package/dist/core/dist/theme/themes/mint-breeze.js +33 -0
- package/dist/core/dist/theme/themes/monokai-pro.js +33 -0
- package/dist/core/dist/theme/themes/night-owl.js +35 -0
- package/dist/core/dist/theme/themes/one-dark-pro.js +33 -0
- package/dist/core/dist/theme/themes/panda.js +33 -0
- package/dist/core/dist/theme/themes/peach-cream.js +33 -0
- package/dist/core/dist/theme/themes/quiet-light.js +37 -0
- package/dist/core/dist/theme/themes/rose-pine-dawn.js +33 -0
- package/dist/core/dist/theme/themes/sand-dune.js +33 -0
- package/dist/core/dist/theme/themes/solarized-light.js +34 -0
- package/dist/core/dist/tooltip-position.js +10 -0
- package/dist/core/dist/utils/color.js +37 -0
- package/dist/core/dist/utils/format.js +29 -0
- package/dist/core/dist/utils/math.js +26 -0
- package/dist/core/dist/utils/poisoned-data-reporter.js +15 -0
- package/dist/core/dist/utils/time.js +41 -0
- package/dist/index.d.ts +1096 -63
- package/dist/index.js +142 -7754
- package/dist/ui/Crosshair.vue.js +53 -0
- package/dist/ui/Crosshair.vue2.js +4 -0
- package/dist/ui/InfoBar.vue.js +143 -0
- package/dist/ui/InfoBar.vue2.js +4 -0
- package/dist/ui/Legend.vue.js +212 -0
- package/dist/ui/Legend.vue2.js +4 -0
- package/dist/ui/Marker.vue.js +47 -0
- package/dist/ui/Marker.vue2.js +4 -0
- package/dist/ui/Navigator.vue.js +58 -0
- package/dist/ui/Navigator.vue2.js +4 -0
- package/dist/ui/NumberFlow.vue.js +69 -0
- package/dist/ui/NumberFlow.vue2.js +4 -0
- package/dist/ui/PieLegend.vue.js +132 -0
- package/dist/ui/PieLegend.vue2.js +4 -0
- package/dist/ui/PieTooltip.vue.js +135 -0
- package/dist/ui/PieTooltip.vue2.js +4 -0
- package/dist/ui/ReferenceLine.vue.js +44 -0
- package/dist/ui/ReferenceLine.vue2.js +4 -0
- package/dist/ui/TimeAxis.vue.js +43 -0
- package/dist/ui/TimeAxis.vue2.js +4 -0
- package/dist/ui/TimeRegion.vue.js +41 -0
- package/dist/ui/TimeRegion.vue2.js +4 -0
- package/dist/ui/Title.vue.js +54 -0
- package/dist/ui/Title.vue2.js +4 -0
- package/dist/ui/Tooltip.vue.js +195 -0
- package/dist/ui/Tooltip.vue2.js +4 -0
- package/dist/ui/YAxis.vue.js +42 -0
- package/dist/ui/YAxis.vue2.js +4 -0
- package/dist/ui/YLabel.vue.js +105 -0
- package/dist/ui/YLabel.vue2.js +4 -0
- package/package.json +2 -2
- package/src/BarSeries.vue +12 -13
- package/src/CandlestickSeries.vue +17 -6
- package/src/LineSeries.vue +12 -13
- package/src/PieSeries.vue +2 -1
- package/src/composables.ts +12 -2
- package/src/index.ts +42 -0
- package/src/ui/Crosshair.vue +28 -3
- package/src/ui/Legend.vue +1 -2
- package/src/ui/Marker.vue +62 -0
- package/src/ui/ReferenceLine.vue +58 -0
- package/src/ui/TimeRegion.vue +54 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { defineComponent as m, ref as p, onMounted as r, watch as s, onUnmounted as o, openBlock as f, createElementBlock as b, normalizeStyle as y, unref as u } from "vue";
|
|
2
|
+
import { mountAxisLabels as x } from "../core/dist/axis/dom-labels.js";
|
|
3
|
+
import { useChartInstance as h } from "../context.js";
|
|
4
|
+
const _ = /* @__PURE__ */ m({
|
|
5
|
+
__name: "YAxis",
|
|
6
|
+
props: {
|
|
7
|
+
format: { type: Function },
|
|
8
|
+
labelCount: {},
|
|
9
|
+
minLabelSpacing: {}
|
|
10
|
+
},
|
|
11
|
+
setup(c) {
|
|
12
|
+
const e = c, n = h(), t = p(null), a = () => n.yScale.setFormat(e.format ?? null);
|
|
13
|
+
r(a), s(() => e.format, a), o(() => n.yScale.setFormat(null));
|
|
14
|
+
const i = () => {
|
|
15
|
+
n.setYAxisLabelDensity({
|
|
16
|
+
labelCount: e.labelCount ?? null,
|
|
17
|
+
minLabelSpacing: e.minLabelSpacing ?? null
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
i(), s(() => [e.labelCount, e.minLabelSpacing], i), o(() => n.setYAxisLabelDensity({ labelCount: null, minLabelSpacing: null }));
|
|
21
|
+
let l = null;
|
|
22
|
+
return r(() => {
|
|
23
|
+
t.value !== null && (l = x({ chart: n, container: t.value, axis: "y" }));
|
|
24
|
+
}), o(() => {
|
|
25
|
+
l?.(), l = null;
|
|
26
|
+
}), (L, S) => (f(), b("div", {
|
|
27
|
+
ref_key: "containerRef",
|
|
28
|
+
ref: t,
|
|
29
|
+
style: y({
|
|
30
|
+
position: "absolute",
|
|
31
|
+
right: "0",
|
|
32
|
+
top: "0",
|
|
33
|
+
bottom: u(n).xAxisHeight + "px",
|
|
34
|
+
width: u(n).yAxisWidth + "px",
|
|
35
|
+
pointerEvents: "none"
|
|
36
|
+
})
|
|
37
|
+
}, null, 4));
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
export {
|
|
41
|
+
_ as default
|
|
42
|
+
};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { defineComponent as B, useSlots as F, computed as n, ref as _, onMounted as T, onUnmounted as z, openBlock as h, createElementBlock as b, Fragment as x, renderSlot as Y, createElementVNode as C, normalizeStyle as L, unref as E, createVNode as N, createCommentVNode as V } from "vue";
|
|
2
|
+
import { useChartInstance as D } from "../context.js";
|
|
3
|
+
import O from "./NumberFlow.vue.js";
|
|
4
|
+
const A = /* @__PURE__ */ B({
|
|
5
|
+
__name: "YLabel",
|
|
6
|
+
props: {
|
|
7
|
+
seriesId: {},
|
|
8
|
+
color: {},
|
|
9
|
+
format: { type: Function }
|
|
10
|
+
},
|
|
11
|
+
setup(S) {
|
|
12
|
+
const u = S, e = D(), k = F(), I = n(() => typeof k.default == "function"), r = _(0), i = () => {
|
|
13
|
+
r.value++;
|
|
14
|
+
};
|
|
15
|
+
T(() => {
|
|
16
|
+
e.setYLabel(!0), e.on("overlayChange", i), e.on("viewportChange", i), e.getSeriesIds().length > 0 && r.value++;
|
|
17
|
+
}), z(() => {
|
|
18
|
+
e.setYLabel(!1), e.off("overlayChange", i), e.off("viewportChange", i);
|
|
19
|
+
});
|
|
20
|
+
function w(a, l) {
|
|
21
|
+
if (l !== void 0) return l;
|
|
22
|
+
const g = a.getSeriesIdsByType("time", { visibleOnly: !0, singleLayerOnly: !0 });
|
|
23
|
+
if (g.length > 0) return g[0];
|
|
24
|
+
const y = a.getSeriesIdsByType("time", { visibleOnly: !0 });
|
|
25
|
+
return y.length > 0 ? y[0] : null;
|
|
26
|
+
}
|
|
27
|
+
const s = n(() => (r.value, w(e, u.seriesId))), t = n(() => (r.value, s.value !== null ? e.getStackedLastValue(s.value) : null)), v = n(() => (r.value, s.value !== null ? e.getPreviousClose(s.value) : null)), o = n(() => (r.value, e.getTheme())), c = n(() => (r.value, t.value === null ? 0 : e.yScale.valueToY(t.value.value))), f = n(() => t.value === null || v.value === null ? "neutral" : t.value.value > v.value ? "up" : t.value.value < v.value ? "down" : "neutral"), d = n(() => {
|
|
28
|
+
if (t.value === null) return o.value.yLabel.neutralBackground;
|
|
29
|
+
if (!t.value.isLive) return o.value.axis.textColor;
|
|
30
|
+
if (u.color) return u.color;
|
|
31
|
+
switch (f.value) {
|
|
32
|
+
case "up":
|
|
33
|
+
return o.value.yLabel.upBackground;
|
|
34
|
+
case "down":
|
|
35
|
+
return o.value.yLabel.downBackground;
|
|
36
|
+
default:
|
|
37
|
+
return o.value.yLabel.neutralBackground;
|
|
38
|
+
}
|
|
39
|
+
}), m = n(() => {
|
|
40
|
+
r.value;
|
|
41
|
+
const a = e.yScale.getRange(), l = a.max - a.min;
|
|
42
|
+
return l < 0.1 ? 6 : l < 10 ? 4 : l < 1e3 ? 2 : 0;
|
|
43
|
+
}), p = n(() => {
|
|
44
|
+
if (u.format) return u.format;
|
|
45
|
+
const a = new Intl.NumberFormat("en-US", {
|
|
46
|
+
minimumFractionDigits: m.value,
|
|
47
|
+
maximumFractionDigits: m.value,
|
|
48
|
+
useGrouping: !1
|
|
49
|
+
});
|
|
50
|
+
return (l) => a.format(l);
|
|
51
|
+
});
|
|
52
|
+
return (a, l) => t.value !== null ? (h(), b(x, { key: 0 }, [
|
|
53
|
+
I.value ? Y(a.$slots, "default", {
|
|
54
|
+
key: 0,
|
|
55
|
+
value: t.value.value,
|
|
56
|
+
y: c.value,
|
|
57
|
+
bgColor: d.value,
|
|
58
|
+
isLive: t.value.isLive,
|
|
59
|
+
direction: f.value,
|
|
60
|
+
format: p.value
|
|
61
|
+
}) : (h(), b(x, { key: 1 }, [
|
|
62
|
+
C("div", {
|
|
63
|
+
style: L({
|
|
64
|
+
position: "absolute",
|
|
65
|
+
left: "0",
|
|
66
|
+
right: E(e).yAxisWidth + "px",
|
|
67
|
+
top: c.value + "px",
|
|
68
|
+
height: "0",
|
|
69
|
+
borderTop: "1px dashed " + d.value,
|
|
70
|
+
opacity: 0.5,
|
|
71
|
+
pointerEvents: "none",
|
|
72
|
+
zIndex: 2
|
|
73
|
+
})
|
|
74
|
+
}, null, 4),
|
|
75
|
+
C("div", {
|
|
76
|
+
style: L({
|
|
77
|
+
position: "absolute",
|
|
78
|
+
right: "4px",
|
|
79
|
+
top: c.value + "px",
|
|
80
|
+
transform: "translateY(-50%)",
|
|
81
|
+
pointerEvents: "auto",
|
|
82
|
+
zIndex: 3,
|
|
83
|
+
background: d.value,
|
|
84
|
+
color: o.value.yLabel.textColor,
|
|
85
|
+
fontSize: o.value.yLabel.fontSize + "px",
|
|
86
|
+
fontFamily: o.value.typography.fontFamily,
|
|
87
|
+
padding: "3px 8px",
|
|
88
|
+
borderRadius: "3px",
|
|
89
|
+
whiteSpace: "nowrap",
|
|
90
|
+
transition: "background-color 0.3s ease"
|
|
91
|
+
})
|
|
92
|
+
}, [
|
|
93
|
+
N(O, {
|
|
94
|
+
value: t.value.value,
|
|
95
|
+
format: p.value,
|
|
96
|
+
"spin-duration": 350
|
|
97
|
+
}, null, 8, ["value", "format"])
|
|
98
|
+
], 4)
|
|
99
|
+
], 64))
|
|
100
|
+
], 64)) : V("", !0);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
export {
|
|
104
|
+
A as default
|
|
105
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wick-charts/vue",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "High-performance canvas timeseries charts for Vue — candlestick, line, bar, pie. Tree-shakeable, zero runtime deps.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"vue": ">=3.3.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@wick-charts/core": "^0.
|
|
48
|
+
"@wick-charts/core": "^0.5.0"
|
|
49
49
|
},
|
|
50
50
|
"scripts": {
|
|
51
51
|
"build": "vite build"
|
package/src/BarSeries.vue
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import type { BarSeriesOptions,
|
|
3
|
-
import { EMPTY_SYNC_STATE,
|
|
2
|
+
import type { BarSeriesOptions, MultiLayerData, SeriesSyncState } from '@wick-charts/core';
|
|
3
|
+
import { BarSeriesDef, EMPTY_SYNC_STATE, syncLayers, toLayers } from '@wick-charts/core';
|
|
4
4
|
import { onMounted, onUnmounted, ref, watch } from 'vue';
|
|
5
5
|
|
|
6
6
|
import { useChartInstance } from './context';
|
|
7
7
|
|
|
8
8
|
const props = defineProps<{
|
|
9
|
-
|
|
9
|
+
/** Flat `TimePoint[]`, `TimePoint[][]`, or named `{ label, color?, data }` layers. Time accepts ms or `Date`. */
|
|
10
|
+
data: MultiLayerData;
|
|
10
11
|
options?: Partial<BarSeriesOptions>;
|
|
11
12
|
/** Stable series ID — same value across remounts. */
|
|
12
13
|
id?: string;
|
|
@@ -15,25 +16,23 @@ const props = defineProps<{
|
|
|
15
16
|
const chart = useChartInstance();
|
|
16
17
|
const seriesId = ref<string | null>(null);
|
|
17
18
|
// Per-layer sync state — drives the shared append/keepLast/update/replace
|
|
18
|
-
// reconciliation so streaming eases instead of
|
|
19
|
+
// reconciliation (plus per-layer labels/colors) so streaming eases instead of
|
|
20
|
+
// snapping Y every tick.
|
|
19
21
|
let prevSync: SeriesSyncState[] = [];
|
|
20
22
|
|
|
21
|
-
function applyData(id: string, data:
|
|
22
|
-
|
|
23
|
-
for (let i = 0; i < data.length; i++) {
|
|
24
|
-
prevSync[i] = syncSeriesLayer({ chart, id, data: data[i], prev: prevSync[i] ?? EMPTY_SYNC_STATE, layerIndex: i });
|
|
25
|
-
}
|
|
26
|
-
});
|
|
23
|
+
function applyData(id: string, data: MultiLayerData): void {
|
|
24
|
+
prevSync = syncLayers({ chart, id, data, prev: prevSync });
|
|
27
25
|
}
|
|
28
26
|
|
|
29
27
|
onMounted(() => {
|
|
30
|
-
const
|
|
28
|
+
const layerCount = toLayers(props.data).length;
|
|
29
|
+
const id = chart.addSeries(BarSeriesDef, {
|
|
31
30
|
...props.options,
|
|
32
|
-
layers:
|
|
31
|
+
layers: layerCount,
|
|
33
32
|
id: props.id,
|
|
34
33
|
});
|
|
35
34
|
seriesId.value = id;
|
|
36
|
-
prevSync =
|
|
35
|
+
prevSync = new Array(layerCount).fill(EMPTY_SYNC_STATE);
|
|
37
36
|
// Lazy watcher — apply initial data here so static-data mounts render without a no-op first frame.
|
|
38
37
|
applyData(id, props.data);
|
|
39
38
|
});
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import type { CandlestickSeriesOptions, OHLCInput, SeriesSyncState } from '@wick-charts/core';
|
|
3
|
-
import { EMPTY_SYNC_STATE, syncSeriesLayer } from '@wick-charts/core';
|
|
2
|
+
import type { CandlestickData, CandlestickSeriesOptions, OHLCInput, SeriesSyncState } from '@wick-charts/core';
|
|
3
|
+
import { CandlestickSeriesDef, EMPTY_SYNC_STATE, syncSeriesLayer, toLayers } from '@wick-charts/core';
|
|
4
4
|
import { onMounted, onUnmounted, ref, watch } from 'vue';
|
|
5
5
|
|
|
6
6
|
import { useChartInstance } from './context';
|
|
7
7
|
|
|
8
8
|
const props = defineProps<{
|
|
9
|
-
|
|
9
|
+
/** Flat `OHLCInput[]`, or `{ label, data }` to name the stream. */
|
|
10
|
+
data: CandlestickData;
|
|
10
11
|
options?: Partial<CandlestickSeriesOptions>;
|
|
11
12
|
/** Stable series ID — same value across remounts. */
|
|
12
13
|
id?: string;
|
|
@@ -19,13 +20,23 @@ const seriesId = ref<string | null>(null);
|
|
|
19
20
|
// burst takes the smooth append path, matching React's 20-candle threshold).
|
|
20
21
|
let prevSync: SeriesSyncState = EMPTY_SYNC_STATE;
|
|
21
22
|
|
|
23
|
+
function applyData(id: string, data: CandlestickData): void {
|
|
24
|
+
const layer = toLayers<OHLCInput>(data)[0];
|
|
25
|
+
chart.setSeriesLabels(id, [layer.label]);
|
|
26
|
+
prevSync = syncSeriesLayer({ chart, id, data: layer.data, prev: prevSync });
|
|
27
|
+
}
|
|
28
|
+
|
|
22
29
|
onMounted(() => {
|
|
23
|
-
const id = chart.addSeries(
|
|
30
|
+
const id = chart.addSeries(CandlestickSeriesDef, {
|
|
31
|
+
...props.options,
|
|
32
|
+
id: props.id,
|
|
33
|
+
label: toLayers<OHLCInput>(props.data)[0].label,
|
|
34
|
+
});
|
|
24
35
|
seriesId.value = id;
|
|
25
36
|
// Initial data load — Vue's `watch` is lazy by default, so the watcher
|
|
26
37
|
// below only fires on subsequent `data` prop mutations. Explicitly apply
|
|
27
38
|
// the first value here so components with static data render immediately.
|
|
28
|
-
|
|
39
|
+
applyData(id, props.data);
|
|
29
40
|
});
|
|
30
41
|
|
|
31
42
|
onUnmounted(() => {
|
|
@@ -38,7 +49,7 @@ watch(
|
|
|
38
49
|
const id = seriesId.value;
|
|
39
50
|
if (!id) return;
|
|
40
51
|
|
|
41
|
-
|
|
52
|
+
applyData(id, data);
|
|
42
53
|
},
|
|
43
54
|
);
|
|
44
55
|
|
package/src/LineSeries.vue
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import type { LineSeriesOptions,
|
|
3
|
-
import { EMPTY_SYNC_STATE,
|
|
2
|
+
import type { LineSeriesOptions, MultiLayerData, SeriesSyncState } from '@wick-charts/core';
|
|
3
|
+
import { EMPTY_SYNC_STATE, LineSeriesDef, syncLayers, toLayers } from '@wick-charts/core';
|
|
4
4
|
import { onMounted, onUnmounted, ref, watch } from 'vue';
|
|
5
5
|
|
|
6
6
|
import { useChartInstance } from './context';
|
|
7
7
|
|
|
8
8
|
const props = defineProps<{
|
|
9
|
-
|
|
9
|
+
/** Flat `TimePoint[]`, `TimePoint[][]`, or named `{ label, color?, data }` layers. Time accepts ms or `Date`. */
|
|
10
|
+
data: MultiLayerData;
|
|
10
11
|
options?: Partial<LineSeriesOptions>;
|
|
11
12
|
/** Stable series ID — same value across remounts. */
|
|
12
13
|
id?: string;
|
|
@@ -15,25 +16,23 @@ const props = defineProps<{
|
|
|
15
16
|
const chart = useChartInstance();
|
|
16
17
|
const seriesId = ref<string | null>(null);
|
|
17
18
|
// Per-layer sync state — drives the shared append/keepLast/update/replace
|
|
18
|
-
// reconciliation so streaming eases instead of
|
|
19
|
+
// reconciliation (plus per-layer labels/colors) so streaming eases instead of
|
|
20
|
+
// snapping Y every tick.
|
|
19
21
|
let prevSync: SeriesSyncState[] = [];
|
|
20
22
|
|
|
21
|
-
function applyData(id: string, data:
|
|
22
|
-
|
|
23
|
-
for (let i = 0; i < data.length; i++) {
|
|
24
|
-
prevSync[i] = syncSeriesLayer({ chart, id, data: data[i], prev: prevSync[i] ?? EMPTY_SYNC_STATE, layerIndex: i });
|
|
25
|
-
}
|
|
26
|
-
});
|
|
23
|
+
function applyData(id: string, data: MultiLayerData): void {
|
|
24
|
+
prevSync = syncLayers({ chart, id, data, prev: prevSync });
|
|
27
25
|
}
|
|
28
26
|
|
|
29
27
|
onMounted(() => {
|
|
30
|
-
const
|
|
28
|
+
const layerCount = toLayers(props.data).length;
|
|
29
|
+
const id = chart.addSeries(LineSeriesDef, {
|
|
31
30
|
...props.options,
|
|
32
|
-
layers:
|
|
31
|
+
layers: layerCount,
|
|
33
32
|
id: props.id,
|
|
34
33
|
});
|
|
35
34
|
seriesId.value = id;
|
|
36
|
-
prevSync =
|
|
35
|
+
prevSync = new Array(layerCount).fill(EMPTY_SYNC_STATE);
|
|
37
36
|
// Lazy watcher — apply initial data here so static-data mounts render without a no-op first frame.
|
|
38
37
|
applyData(id, props.data);
|
|
39
38
|
});
|
package/src/PieSeries.vue
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { PieSeriesOptions, PieSliceData } from '@wick-charts/core';
|
|
3
|
+
import { PieSeriesDef } from '@wick-charts/core';
|
|
3
4
|
import { onMounted, onUnmounted, ref, watch } from 'vue';
|
|
4
5
|
|
|
5
6
|
import { useChartInstance } from './context';
|
|
@@ -15,7 +16,7 @@ const chart = useChartInstance();
|
|
|
15
16
|
const seriesId = ref<string | null>(null);
|
|
16
17
|
|
|
17
18
|
onMounted(() => {
|
|
18
|
-
const id = chart.addSeries(
|
|
19
|
+
const id = chart.addSeries(PieSeriesDef, { ...props.options, id: props.id });
|
|
19
20
|
seriesId.value = id;
|
|
20
21
|
// Lazy watcher — apply initial data here so static-data mounts render without a no-op first frame.
|
|
21
22
|
if (props.data.length > 0) {
|
package/src/composables.ts
CHANGED
|
@@ -22,10 +22,20 @@ export function useYRange(chart: ChartInstance): Ref<YRange> {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export function useLastYValue(chart: ChartInstance, seriesId: string): Ref<{ value: number; isLive: boolean } | null> {
|
|
25
|
-
|
|
25
|
+
let snapshot = chart.getLastValue(seriesId);
|
|
26
|
+
// Track the pixel Y the snapshot maps to so a pan/zoom/resize that leaves the
|
|
27
|
+
// value unchanged but shifts the badge still re-emits — a consumer-positioned
|
|
28
|
+
// badge reads `yScale.valueToY` off this ref. Comparing prev/next against the
|
|
29
|
+
// live yScale alone would always be equal.
|
|
30
|
+
let lastY = snapshot ? chart.yScale.valueToY(snapshot.value) : null;
|
|
31
|
+
const val = ref<{ value: number; isLive: boolean } | null>(snapshot);
|
|
26
32
|
const handler = () => {
|
|
27
33
|
const next = chart.getLastValue(seriesId);
|
|
28
|
-
|
|
34
|
+
const nextY = next ? chart.yScale.valueToY(next.value) : null;
|
|
35
|
+
if (snapshot?.value === next?.value && snapshot?.isLive === next?.isLive && lastY === nextY) return;
|
|
36
|
+
|
|
37
|
+
snapshot = next;
|
|
38
|
+
lastY = nextY;
|
|
29
39
|
val.value = next;
|
|
30
40
|
};
|
|
31
41
|
onMounted(() => {
|
package/src/index.ts
CHANGED
|
@@ -11,19 +11,29 @@
|
|
|
11
11
|
export type {
|
|
12
12
|
AxisBound,
|
|
13
13
|
AxisConfig,
|
|
14
|
+
BarPaintArgs,
|
|
15
|
+
BarPainter,
|
|
14
16
|
BarSeriesOptions,
|
|
15
17
|
/** @deprecated Use {@link StackingMode} instead. */
|
|
16
18
|
BuildHoverSnapshotsArgs,
|
|
17
19
|
BuildLastSnapshotsArgs,
|
|
20
|
+
CandlePaintArgs,
|
|
21
|
+
CandlePainter,
|
|
18
22
|
CandlestickSeriesOptions,
|
|
19
23
|
ChartLayout,
|
|
20
24
|
ChartOptions,
|
|
21
25
|
ChartTheme,
|
|
26
|
+
CornerMask,
|
|
22
27
|
CrosshairPosition,
|
|
28
|
+
CurveKind,
|
|
23
29
|
HoverInfo,
|
|
24
30
|
LegendItem,
|
|
31
|
+
LinePaintArgs,
|
|
32
|
+
LinePainter,
|
|
25
33
|
/** @deprecated Use {@link TimePoint} instead. */
|
|
26
34
|
LineSeriesOptions,
|
|
35
|
+
MarkerConfig,
|
|
36
|
+
MarkerShape,
|
|
27
37
|
NavigatorCandlePoint,
|
|
28
38
|
NavigatorControllerParams,
|
|
29
39
|
NavigatorData,
|
|
@@ -32,8 +42,18 @@ export type {
|
|
|
32
42
|
NavigatorSeriesType,
|
|
33
43
|
OHLCData,
|
|
34
44
|
OHLCInput,
|
|
45
|
+
PaintEnv,
|
|
46
|
+
PerfConfig,
|
|
47
|
+
PerfMonitorOptions,
|
|
48
|
+
PerfStats,
|
|
35
49
|
PieSeriesOptions,
|
|
36
50
|
PieSliceData,
|
|
51
|
+
ReferenceLineConfig,
|
|
52
|
+
ReferenceLineOrientation,
|
|
53
|
+
ReferenceLineStyle,
|
|
54
|
+
RoundedRectArgs,
|
|
55
|
+
SeriesCreateEnv,
|
|
56
|
+
SeriesDefinition,
|
|
37
57
|
SeriesSnapshot,
|
|
38
58
|
SeriesType,
|
|
39
59
|
SliceInfo,
|
|
@@ -43,11 +63,14 @@ export type {
|
|
|
43
63
|
ThemePreset,
|
|
44
64
|
TimePoint,
|
|
45
65
|
TimePointInput,
|
|
66
|
+
TimeRegionConfig,
|
|
67
|
+
TimeRegionEnd,
|
|
46
68
|
TimeValue,
|
|
47
69
|
TooltipField,
|
|
48
70
|
TooltipFormatter,
|
|
49
71
|
TooltipPosition,
|
|
50
72
|
TooltipPositionArgs,
|
|
73
|
+
TracePathArgs,
|
|
51
74
|
Typography,
|
|
52
75
|
ValueFormatter,
|
|
53
76
|
VisibleRange,
|
|
@@ -55,9 +78,15 @@ export type {
|
|
|
55
78
|
YAxisConfig,
|
|
56
79
|
YRange,
|
|
57
80
|
} from '@wick-charts/core';
|
|
81
|
+
// Painters — custom per-element drawing for bar / candle / line series.
|
|
58
82
|
export {
|
|
83
|
+
BarSeriesDef,
|
|
84
|
+
CandlestickSeriesDef,
|
|
59
85
|
ChartInstance,
|
|
86
|
+
LineSeriesDef,
|
|
60
87
|
NavigatorController,
|
|
88
|
+
PerfMonitor,
|
|
89
|
+
PieSeriesDef,
|
|
61
90
|
andromeda,
|
|
62
91
|
autoGradient,
|
|
63
92
|
ayuMirage,
|
|
@@ -68,6 +97,7 @@ export {
|
|
|
68
97
|
createTheme,
|
|
69
98
|
detectInterval,
|
|
70
99
|
dracula,
|
|
100
|
+
fillRoundedRect,
|
|
71
101
|
formatCompact,
|
|
72
102
|
formatDate,
|
|
73
103
|
formatPriceAdaptive,
|
|
@@ -87,13 +117,22 @@ export {
|
|
|
87
117
|
oneDarkPro,
|
|
88
118
|
panda,
|
|
89
119
|
peachCream,
|
|
120
|
+
perfHud,
|
|
90
121
|
quietLight,
|
|
122
|
+
registerBuiltinSeries,
|
|
123
|
+
registerSeriesDefinition,
|
|
91
124
|
resolveAxisFontSize,
|
|
92
125
|
resolveAxisTextColor,
|
|
93
126
|
resolveCandlestickBodyColor,
|
|
94
127
|
rosePineDawn,
|
|
128
|
+
roundedBarFill,
|
|
129
|
+
roundedCandleFill,
|
|
95
130
|
sandDune,
|
|
131
|
+
smoothCurve,
|
|
96
132
|
solarizedLight,
|
|
133
|
+
steppedCurve,
|
|
134
|
+
straightCurve,
|
|
135
|
+
tracePath,
|
|
97
136
|
} from '@wick-charts/core';
|
|
98
137
|
|
|
99
138
|
export { default as BarSeries } from './BarSeries.vue';
|
|
@@ -108,11 +147,14 @@ export { default as PieSeries } from './PieSeries.vue';
|
|
|
108
147
|
export { default as Crosshair } from './ui/Crosshair.vue';
|
|
109
148
|
export { default as InfoBar } from './ui/InfoBar.vue';
|
|
110
149
|
export { default as Legend } from './ui/Legend.vue';
|
|
150
|
+
export { default as Marker } from './ui/Marker.vue';
|
|
111
151
|
export { default as Navigator } from './ui/Navigator.vue';
|
|
112
152
|
export { default as NumberFlow } from './ui/NumberFlow.vue';
|
|
113
153
|
export { default as PieLegend } from './ui/PieLegend.vue';
|
|
114
154
|
export { default as PieTooltip } from './ui/PieTooltip.vue';
|
|
155
|
+
export { default as ReferenceLine } from './ui/ReferenceLine.vue';
|
|
115
156
|
export { default as TimeAxis } from './ui/TimeAxis.vue';
|
|
157
|
+
export { default as TimeRegion } from './ui/TimeRegion.vue';
|
|
116
158
|
export { default as Title } from './ui/Title.vue';
|
|
117
159
|
// UI overlays
|
|
118
160
|
export { default as Tooltip } from './ui/Tooltip.vue';
|
package/src/ui/Crosshair.vue
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { formatTime } from '@wick-charts/core';
|
|
3
|
-
import { computed } from 'vue';
|
|
3
|
+
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
|
4
4
|
|
|
5
5
|
import { useCrosshairPosition } from '../composables';
|
|
6
6
|
import { useChartInstance } from '../context';
|
|
@@ -8,8 +8,33 @@ import { useChartInstance } from '../context';
|
|
|
8
8
|
const chart = useChartInstance();
|
|
9
9
|
const position = useCrosshairPosition(chart);
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
// `setTheme` fires `overlayChange`; bump re-runs the theme computed so the
|
|
12
|
+
// label colors track a runtime theme swap instead of freezing on the
|
|
13
|
+
// first-render snapshot (matches Tooltip/PieTooltip).
|
|
14
|
+
const bump = ref(0);
|
|
15
|
+
const onOverlayChange = () => {
|
|
16
|
+
bump.value++;
|
|
17
|
+
};
|
|
18
|
+
onMounted(() => chart.on('overlayChange', onOverlayChange));
|
|
19
|
+
onUnmounted(() => chart.off('overlayChange', onOverlayChange));
|
|
20
|
+
|
|
21
|
+
const theme = computed(() => {
|
|
22
|
+
void bump.value;
|
|
23
|
+
|
|
24
|
+
return chart.getTheme();
|
|
25
|
+
});
|
|
12
26
|
const dataInterval = computed(() => chart.getDataInterval());
|
|
27
|
+
// Format the time pill at the axis's *resolved* tick granularity, not the raw
|
|
28
|
+
// data interval — otherwise a time-of-day badge floats among date labels when
|
|
29
|
+
// zoomed out. `niceTickValues` reads the live visible range, which Vue can't
|
|
30
|
+
// track reactively, so depend on `position` (a wheel-zoom re-emits the
|
|
31
|
+
// crosshair) to re-resolve on zoom — matching React, which recomputes this on
|
|
32
|
+
// every render. Falls back to dataInterval on a degenerate range.
|
|
33
|
+
const tickInterval = computed(() => {
|
|
34
|
+
void position.value;
|
|
35
|
+
|
|
36
|
+
return chart.timeScale.niceTickValues(dataInterval.value).tickInterval || dataInterval.value;
|
|
37
|
+
});
|
|
13
38
|
|
|
14
39
|
const labelStyle = computed(() => ({
|
|
15
40
|
// Blend the theme's labelBackground at 80% opacity so the axis grid
|
|
@@ -54,7 +79,7 @@ const labelStyle = computed(() => ({
|
|
|
54
79
|
...labelStyle,
|
|
55
80
|
}"
|
|
56
81
|
>
|
|
57
|
-
{{ formatTime(position.time,
|
|
82
|
+
{{ formatTime(position.time, tickInterval) }}
|
|
58
83
|
</div>
|
|
59
84
|
</template>
|
|
60
85
|
</template>
|
package/src/ui/Legend.vue
CHANGED
|
@@ -129,7 +129,6 @@ function buildLegendItems(c: ChartInstance): LegendItem[] {
|
|
|
129
129
|
for (const seriesId of c.getSeriesIds()) {
|
|
130
130
|
const layers = c.getSeriesLayers(seriesId);
|
|
131
131
|
if (layers) {
|
|
132
|
-
const baseLabel = c.getSeriesLabel(seriesId);
|
|
133
132
|
for (let layerIndex = 0; layerIndex < layers.length; layerIndex++) {
|
|
134
133
|
const id = `${seriesId}_layer${layerIndex}`;
|
|
135
134
|
const visible = c.isSeriesVisible(seriesId) && c.isLayerVisible(seriesId, layerIndex);
|
|
@@ -138,7 +137,7 @@ function buildLegendItems(c: ChartInstance): LegendItem[] {
|
|
|
138
137
|
id,
|
|
139
138
|
seriesId,
|
|
140
139
|
layerIndex,
|
|
141
|
-
label:
|
|
140
|
+
label: c.getLayerLabel(seriesId, layerIndex) ?? `Series ${layerIndex + 1}`,
|
|
142
141
|
color: layers[layerIndex].color,
|
|
143
142
|
isDisabled: !visible,
|
|
144
143
|
}),
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { MarkerShape, TimeValue } from '@wick-charts/core';
|
|
3
|
+
import { onMounted, onUnmounted, watch } from 'vue';
|
|
4
|
+
|
|
5
|
+
import { useChartInstance } from '../context';
|
|
6
|
+
|
|
7
|
+
const props = defineProps<{
|
|
8
|
+
/** X anchor — epoch ms or a `Date`. */
|
|
9
|
+
time: TimeValue;
|
|
10
|
+
/** Y anchor as a data value. Omit and set `seriesId` to snap to that series' value at `time`. */
|
|
11
|
+
value?: number;
|
|
12
|
+
/** Snap Y to this series' value at `time` when `value` is omitted. Also the default color source. */
|
|
13
|
+
seriesId?: string;
|
|
14
|
+
/** Glyph at the anchor. Default: `'dot'`. Arrows point their tip at the anchor. */
|
|
15
|
+
shape?: MarkerShape;
|
|
16
|
+
/** Reuse the line pulse halo around the anchor. Default: `false`. */
|
|
17
|
+
pulse?: boolean;
|
|
18
|
+
/** Optional text label drawn in a pill next to the anchor. */
|
|
19
|
+
label?: string;
|
|
20
|
+
/** Color override. Falls back to the series color (when `seriesId` is set), then the theme line color. */
|
|
21
|
+
color?: string;
|
|
22
|
+
}>();
|
|
23
|
+
|
|
24
|
+
const chart = useChartInstance();
|
|
25
|
+
let markerId: string | null = null;
|
|
26
|
+
|
|
27
|
+
onMounted(() => {
|
|
28
|
+
markerId = chart.addMarker({
|
|
29
|
+
time: props.time,
|
|
30
|
+
value: props.value,
|
|
31
|
+
seriesId: props.seriesId,
|
|
32
|
+
shape: props.shape,
|
|
33
|
+
pulse: props.pulse,
|
|
34
|
+
label: props.label,
|
|
35
|
+
color: props.color,
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
onUnmounted(() => {
|
|
40
|
+
if (markerId !== null) chart.removeMarker(markerId);
|
|
41
|
+
markerId = null;
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
watch(
|
|
45
|
+
() => [props.time, props.value, props.seriesId, props.shape, props.pulse, props.label, props.color],
|
|
46
|
+
() => {
|
|
47
|
+
if (markerId === null) return;
|
|
48
|
+
|
|
49
|
+
chart.updateMarker(markerId, {
|
|
50
|
+
time: props.time,
|
|
51
|
+
value: props.value,
|
|
52
|
+
seriesId: props.seriesId,
|
|
53
|
+
shape: props.shape,
|
|
54
|
+
pulse: props.pulse,
|
|
55
|
+
label: props.label,
|
|
56
|
+
color: props.color,
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
);
|
|
60
|
+
</script>
|
|
61
|
+
|
|
62
|
+
<template><span v-if="false" /></template>
|