@qtsurfer/sveltecharts 0.5.0 → 0.7.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/dist/LightweightTimeSeriesChartBuilder.d.ts +58 -0
- package/dist/LightweightTimeSeriesChartBuilder.d.ts.map +1 -0
- package/dist/LightweightTimeSeriesChartBuilder.js +465 -0
- package/dist/SVELightweightCharts.svelte +250 -0
- package/dist/SVELightweightCharts.svelte.d.ts +15 -0
- package/dist/SVELightweightCharts.svelte.d.ts.map +1 -0
- package/dist/TimeSeriesChartBuilder.d.ts +7 -10
- package/dist/TimeSeriesChartBuilder.d.ts.map +1 -1
- package/dist/TimeSeriesChartBuilder.js +60 -3
- package/dist/chartAdapter.d.ts +31 -0
- package/dist/chartAdapter.d.ts.map +1 -0
- package/dist/chartAdapter.js +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -1
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type IChartApi } from 'lightweight-charts';
|
|
2
|
+
import type { ChartDataset, ChartDatasetFormatSimpleObject, ChartMarkerPointOptions, TimeSeriesChartAdapter } from './chartAdapter';
|
|
3
|
+
type ConfigBuilder = {
|
|
4
|
+
externalManagerLegend?: boolean;
|
|
5
|
+
};
|
|
6
|
+
export declare function getPricePrecision(values: number[]): number;
|
|
7
|
+
export declare function formatPreciseValue(value: number): string;
|
|
8
|
+
export declare class LightweightTimeSeriesChartBuilder implements TimeSeriesChartAdapter {
|
|
9
|
+
LightweightChart: IChartApi;
|
|
10
|
+
private builderConfig;
|
|
11
|
+
private dataset;
|
|
12
|
+
private yDimensions;
|
|
13
|
+
private yDimensionNames;
|
|
14
|
+
private _tsColumn;
|
|
15
|
+
private selected;
|
|
16
|
+
private series;
|
|
17
|
+
private markersPlugins;
|
|
18
|
+
private markers;
|
|
19
|
+
private _dataDirty;
|
|
20
|
+
private _building;
|
|
21
|
+
private _timeRangeForced;
|
|
22
|
+
constructor(instance: IChartApi, builderConfig?: ConfigBuilder);
|
|
23
|
+
setLegendIcon(_icon: string): this;
|
|
24
|
+
setDataset(data: ChartDataset, yDimensionsNames?: string[]): this;
|
|
25
|
+
addDimension(data: ChartDatasetFormatSimpleObject, dimName: string): this;
|
|
26
|
+
addMarkerPoint(id: number, data: {
|
|
27
|
+
dimName: string;
|
|
28
|
+
timestamp: number;
|
|
29
|
+
name?: string;
|
|
30
|
+
}, options?: ChartMarkerPointOptions): this;
|
|
31
|
+
build(): this;
|
|
32
|
+
getLegendStatus(): Record<string, boolean>;
|
|
33
|
+
toggleLegend(column: string): this;
|
|
34
|
+
goToZoom(start: number, end: number): this;
|
|
35
|
+
scrollToTime(timestamp: number): this;
|
|
36
|
+
getTotalRows(): number;
|
|
37
|
+
getRangeValues(): [number, number];
|
|
38
|
+
toggleMarkers(id: number, dimName: string, shape: string): this;
|
|
39
|
+
clearMarkers(): this;
|
|
40
|
+
private normalizeDataset;
|
|
41
|
+
private normalizeSimpleObject;
|
|
42
|
+
private normalizeObjectArray;
|
|
43
|
+
private normalizeMatrix;
|
|
44
|
+
private createSeriesData;
|
|
45
|
+
private addSeries;
|
|
46
|
+
private syncSeriesMarkers;
|
|
47
|
+
private expandTimeRangeForMarkers;
|
|
48
|
+
private clearSeries;
|
|
49
|
+
private toLineData;
|
|
50
|
+
private toChartTime;
|
|
51
|
+
private forceTimeRangeToDataset;
|
|
52
|
+
private isPercentageDimension;
|
|
53
|
+
private syncPriceScales;
|
|
54
|
+
private mapMarkerShape;
|
|
55
|
+
private mapMarkerPosition;
|
|
56
|
+
}
|
|
57
|
+
export {};
|
|
58
|
+
//# sourceMappingURL=LightweightTimeSeriesChartBuilder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LightweightTimeSeriesChartBuilder.d.ts","sourceRoot":"","sources":["../src/lib/LightweightTimeSeriesChartBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,EAGN,KAAK,SAAS,EASd,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EACX,YAAY,EAGZ,8BAA8B,EAC9B,uBAAuB,EACvB,sBAAsB,EACtB,MAAM,gBAAgB,CAAC;AAExB,KAAK,aAAa,GAAG;IACpB,qBAAqB,CAAC,EAAE,OAAO,CAAC;CAChC,CAAC;AA6BF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,UAWjD;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,UAW/C;AAED,qBAAa,iCAAkC,YAAW,sBAAsB;IACxE,gBAAgB,EAAE,SAAS,CAAC;IACnC,OAAO,CAAC,aAAa,CAEnB;IACF,OAAO,CAAC,OAAO,CAAsC;IACrD,OAAO,CAAC,WAAW,CAAgB;IACnC,OAAO,CAAC,eAAe,CAAgB;IACvC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAA+B;IAC/C,OAAO,CAAC,MAAM,CAA+C;IAC7D,OAAO,CAAC,cAAc,CAAoD;IAC1E,OAAO,CAAC,OAAO,CAAoC;IACnD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,gBAAgB,CAAS;gBAErB,QAAQ,EAAE,SAAS,EAAE,aAAa,CAAC,EAAE,aAAa;IAK9D,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIlC,UAAU,CAAC,IAAI,EAAE,YAAY,EAAE,gBAAgB,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI;IAejE,YAAY,CAAC,IAAI,EAAE,8BAA8B,EAAE,OAAO,EAAE,MAAM;IAclE,cAAc,CACb,EAAE,EAAE,MAAM,EACV,IAAI,EAAE;QACL,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,CAAC;KACd,EACD,OAAO,CAAC,EAAE,uBAAuB,GAC/B,IAAI;IAmBP,KAAK;IAqCL,eAAe;IAIf,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAWlC,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAuB1C,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAqBrC,YAAY;IAIZ,cAAc,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC;IAQlC,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAmBxD,YAAY,IAAI,IAAI;IAQpB,OAAO,CAAC,gBAAgB;IAWxB,OAAO,CAAC,qBAAqB;IAqB7B,OAAO,CAAC,oBAAoB;IA2B5B,OAAO,CAAC,eAAe;IAsCvB,OAAO,CAAC,gBAAgB;IAmBxB,OAAO,CAAC,SAAS;IAuBjB,OAAO,CAAC,iBAAiB;IAoCzB,OAAO,CAAC,yBAAyB;IA4BjC,OAAO,CAAC,WAAW;IAgBnB,OAAO,CAAC,UAAU;IAsBlB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,uBAAuB;IA4B/B,OAAO,CAAC,qBAAqB;IAI7B,OAAO,CAAC,eAAe;IAcvB,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,iBAAiB;CAWzB"}
|
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
import { LineSeries, createSeriesMarkers } from 'lightweight-charts';
|
|
2
|
+
const SERIES_COLORS = ['#2563eb', '#16a34a', '#dc2626', '#7c3aed', '#d97706', '#0891b2'];
|
|
3
|
+
const DEFAULT_PRICE_PRECISION = 2;
|
|
4
|
+
const MAX_PRICE_PRECISION = 12;
|
|
5
|
+
function getDecimalPrecision(value) {
|
|
6
|
+
if (!Number.isFinite(value) || value === 0) {
|
|
7
|
+
return 0;
|
|
8
|
+
}
|
|
9
|
+
const [mantissa, exponentValue] = Math.abs(value).toExponential(MAX_PRICE_PRECISION).split('e');
|
|
10
|
+
const exponent = Number(exponentValue);
|
|
11
|
+
const significantDecimals = (mantissa.split('.')[1] ?? '').replace(/0+$/, '').length;
|
|
12
|
+
return Math.max(0, Math.min(MAX_PRICE_PRECISION, significantDecimals - exponent));
|
|
13
|
+
}
|
|
14
|
+
export function getPricePrecision(values) {
|
|
15
|
+
let precision = DEFAULT_PRICE_PRECISION;
|
|
16
|
+
for (const value of values) {
|
|
17
|
+
precision = Math.max(precision, getDecimalPrecision(value));
|
|
18
|
+
if (precision >= MAX_PRICE_PRECISION) {
|
|
19
|
+
return MAX_PRICE_PRECISION;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return precision;
|
|
23
|
+
}
|
|
24
|
+
export function formatPreciseValue(value) {
|
|
25
|
+
if (!Number.isFinite(value)) {
|
|
26
|
+
return String(value);
|
|
27
|
+
}
|
|
28
|
+
const precision = getDecimalPrecision(value);
|
|
29
|
+
if (precision === 0) {
|
|
30
|
+
return value.toString();
|
|
31
|
+
}
|
|
32
|
+
return value.toFixed(precision).replace(/\.?0+$/, '');
|
|
33
|
+
}
|
|
34
|
+
export class LightweightTimeSeriesChartBuilder {
|
|
35
|
+
LightweightChart;
|
|
36
|
+
builderConfig = {
|
|
37
|
+
externalManagerLegend: false
|
|
38
|
+
};
|
|
39
|
+
dataset = {};
|
|
40
|
+
yDimensions = [];
|
|
41
|
+
yDimensionNames = [];
|
|
42
|
+
_tsColumn = '_ts';
|
|
43
|
+
selected = {};
|
|
44
|
+
series = new Map();
|
|
45
|
+
markersPlugins = new Map();
|
|
46
|
+
markers = new Map();
|
|
47
|
+
_dataDirty = false;
|
|
48
|
+
_building = false;
|
|
49
|
+
_timeRangeForced = false;
|
|
50
|
+
constructor(instance, builderConfig) {
|
|
51
|
+
this.LightweightChart = instance;
|
|
52
|
+
this.builderConfig = { ...this.builderConfig, ...builderConfig };
|
|
53
|
+
}
|
|
54
|
+
setLegendIcon(_icon) {
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
57
|
+
setDataset(data, yDimensionsNames) {
|
|
58
|
+
const normalized = this.normalizeDataset(data, yDimensionsNames);
|
|
59
|
+
this.clearSeries();
|
|
60
|
+
this.dataset = normalized.dataset;
|
|
61
|
+
this.yDimensions = normalized.yDimensions;
|
|
62
|
+
this.yDimensionNames = normalized.yDimensionNames;
|
|
63
|
+
this._tsColumn = normalized.tsColumn;
|
|
64
|
+
this.selected = {};
|
|
65
|
+
this._dataDirty = true;
|
|
66
|
+
this.createSeriesData();
|
|
67
|
+
return this.build();
|
|
68
|
+
}
|
|
69
|
+
addDimension(data, dimName) {
|
|
70
|
+
if (!this.dataset[this._tsColumn]?.length) {
|
|
71
|
+
throw new Error('No source data or dimensions found. Before loading data');
|
|
72
|
+
}
|
|
73
|
+
this.dataset[dimName] = data[dimName];
|
|
74
|
+
this.yDimensions.push(dimName);
|
|
75
|
+
this.yDimensionNames.push(dimName);
|
|
76
|
+
this._dataDirty = true;
|
|
77
|
+
this.addSeries(dimName, dimName, true);
|
|
78
|
+
return this.build();
|
|
79
|
+
}
|
|
80
|
+
addMarkerPoint(id, data, options) {
|
|
81
|
+
const markerState = {
|
|
82
|
+
id,
|
|
83
|
+
time: this.toChartTime(data.timestamp),
|
|
84
|
+
color: options?.color ?? '#000000',
|
|
85
|
+
shape: this.mapMarkerShape(options?.icon),
|
|
86
|
+
position: this.mapMarkerPosition(options?.position),
|
|
87
|
+
text: data.name,
|
|
88
|
+
visible: true,
|
|
89
|
+
size: options?.symbolSize ?? 4
|
|
90
|
+
};
|
|
91
|
+
const markers = this.markers.get(data.dimName) ?? [];
|
|
92
|
+
markers.push(markerState);
|
|
93
|
+
this.markers.set(data.dimName, markers);
|
|
94
|
+
this.syncSeriesMarkers(data.dimName);
|
|
95
|
+
return this;
|
|
96
|
+
}
|
|
97
|
+
build() {
|
|
98
|
+
if (this._building) {
|
|
99
|
+
return this;
|
|
100
|
+
}
|
|
101
|
+
this._building = true;
|
|
102
|
+
const visibleLogicalRange = this.LightweightChart.timeScale().getVisibleLogicalRange();
|
|
103
|
+
const rebuildData = this._dataDirty;
|
|
104
|
+
this._dataDirty = false;
|
|
105
|
+
if (rebuildData) {
|
|
106
|
+
for (const dim of this.yDimensions) {
|
|
107
|
+
const series = this.series.get(dim);
|
|
108
|
+
if (!series) {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
const lineData = this.toLineData(dim);
|
|
112
|
+
series.setData(lineData);
|
|
113
|
+
series.applyOptions({ visible: this.selected[dim] ?? false });
|
|
114
|
+
}
|
|
115
|
+
if (visibleLogicalRange) {
|
|
116
|
+
this.LightweightChart.timeScale().setVisibleLogicalRange(visibleLogicalRange);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
this.LightweightChart.timeScale().fitContent();
|
|
120
|
+
this.forceTimeRangeToDataset();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
for (const dim of this.yDimensions) {
|
|
124
|
+
this.syncSeriesMarkers(dim);
|
|
125
|
+
}
|
|
126
|
+
this._building = false;
|
|
127
|
+
return this;
|
|
128
|
+
}
|
|
129
|
+
getLegendStatus() {
|
|
130
|
+
return this.selected;
|
|
131
|
+
}
|
|
132
|
+
toggleLegend(column) {
|
|
133
|
+
if (!column || !this.series.has(column)) {
|
|
134
|
+
return this;
|
|
135
|
+
}
|
|
136
|
+
const nextVisible = !(this.selected[column] ?? false);
|
|
137
|
+
this.selected[column] = nextVisible;
|
|
138
|
+
this.series.get(column)?.applyOptions({ visible: nextVisible });
|
|
139
|
+
return this;
|
|
140
|
+
}
|
|
141
|
+
goToZoom(start, end) {
|
|
142
|
+
const [min, max] = this.getRangeValues();
|
|
143
|
+
if (!min && !max) {
|
|
144
|
+
return this;
|
|
145
|
+
}
|
|
146
|
+
if (start <= 0 && end >= 100) {
|
|
147
|
+
this.LightweightChart.timeScale().fitContent();
|
|
148
|
+
return this;
|
|
149
|
+
}
|
|
150
|
+
const width = max - min;
|
|
151
|
+
const from = min + width * (start / 100);
|
|
152
|
+
const to = min + width * (end / 100);
|
|
153
|
+
this.LightweightChart.timeScale().setVisibleRange({
|
|
154
|
+
from: this.toChartTime(from),
|
|
155
|
+
to: this.toChartTime(to)
|
|
156
|
+
});
|
|
157
|
+
return this;
|
|
158
|
+
}
|
|
159
|
+
scrollToTime(timestamp) {
|
|
160
|
+
const timeSec = this.toChartTime(timestamp);
|
|
161
|
+
const timeScale = this.LightweightChart.timeScale();
|
|
162
|
+
const visibleRange = timeScale.getVisibleRange();
|
|
163
|
+
if (!visibleRange) {
|
|
164
|
+
timeScale.setVisibleRange({ from: timeSec, to: (timeSec + 60) });
|
|
165
|
+
return this;
|
|
166
|
+
}
|
|
167
|
+
const width = Number(visibleRange.to) - Number(visibleRange.from);
|
|
168
|
+
const halfWidth = Math.max(width / 2, 30);
|
|
169
|
+
timeScale.setVisibleRange({
|
|
170
|
+
from: (timeSec - halfWidth),
|
|
171
|
+
to: (timeSec + halfWidth)
|
|
172
|
+
});
|
|
173
|
+
return this;
|
|
174
|
+
}
|
|
175
|
+
getTotalRows() {
|
|
176
|
+
return this.dataset[this._tsColumn]?.length ?? 0;
|
|
177
|
+
}
|
|
178
|
+
getRangeValues() {
|
|
179
|
+
const timestamps = this.dataset[this._tsColumn] ?? [];
|
|
180
|
+
if (!timestamps.length) {
|
|
181
|
+
return [0, 0];
|
|
182
|
+
}
|
|
183
|
+
return [timestamps[0], timestamps[timestamps.length - 1]];
|
|
184
|
+
}
|
|
185
|
+
toggleMarkers(id, dimName, shape) {
|
|
186
|
+
const markers = this.markers.get(dimName);
|
|
187
|
+
if (!markers) {
|
|
188
|
+
return this;
|
|
189
|
+
}
|
|
190
|
+
const marker = markers.find((item) => item.id === id);
|
|
191
|
+
if (!marker) {
|
|
192
|
+
return this;
|
|
193
|
+
}
|
|
194
|
+
marker.visible = !marker.visible;
|
|
195
|
+
if (marker.visible) {
|
|
196
|
+
marker.shape = this.mapMarkerShape(shape);
|
|
197
|
+
}
|
|
198
|
+
this.syncSeriesMarkers(dimName);
|
|
199
|
+
return this;
|
|
200
|
+
}
|
|
201
|
+
clearMarkers() {
|
|
202
|
+
this.markers.clear();
|
|
203
|
+
for (const plugin of this.markersPlugins.values()) {
|
|
204
|
+
plugin.setMarkers([]);
|
|
205
|
+
}
|
|
206
|
+
return this;
|
|
207
|
+
}
|
|
208
|
+
normalizeDataset(data, yDimensionsNames) {
|
|
209
|
+
if (Array.isArray(data)) {
|
|
210
|
+
if (!data.length || Array.isArray(data[0])) {
|
|
211
|
+
return this.normalizeMatrix(data, yDimensionsNames);
|
|
212
|
+
}
|
|
213
|
+
return this.normalizeObjectArray(data, yDimensionsNames);
|
|
214
|
+
}
|
|
215
|
+
return this.normalizeSimpleObject(data, yDimensionsNames);
|
|
216
|
+
}
|
|
217
|
+
normalizeSimpleObject(data, yDimensionsNames) {
|
|
218
|
+
const keys = Object.keys(data);
|
|
219
|
+
const tsColumn = keys.shift();
|
|
220
|
+
if (!tsColumn) {
|
|
221
|
+
throw new Error('No time dimension found.');
|
|
222
|
+
}
|
|
223
|
+
if (yDimensionsNames?.length && yDimensionsNames.length !== keys.length + 1) {
|
|
224
|
+
throw new Error(`Dimensions length ${yDimensionsNames.length} does not match total columns ${keys.length + 1}.`);
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
tsColumn,
|
|
228
|
+
dataset: data,
|
|
229
|
+
yDimensions: keys,
|
|
230
|
+
yDimensionNames: yDimensionsNames?.length ? yDimensionsNames.slice(1) : keys
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
normalizeObjectArray(data, yDimensionsNames) {
|
|
234
|
+
if (data.length < 2) {
|
|
235
|
+
throw new Error('Minimum data length is 2.');
|
|
236
|
+
}
|
|
237
|
+
const keys = Object.keys(data[0]);
|
|
238
|
+
const tsColumn = keys.shift();
|
|
239
|
+
if (!tsColumn) {
|
|
240
|
+
throw new Error('No time dimension found.');
|
|
241
|
+
}
|
|
242
|
+
const dataset = {
|
|
243
|
+
[tsColumn]: data.map((row) => Number(row[tsColumn]))
|
|
244
|
+
};
|
|
245
|
+
for (const key of keys) {
|
|
246
|
+
dataset[key] = data.map((row) => Number(row[key]));
|
|
247
|
+
}
|
|
248
|
+
return {
|
|
249
|
+
tsColumn,
|
|
250
|
+
dataset,
|
|
251
|
+
yDimensions: keys,
|
|
252
|
+
yDimensionNames: yDimensionsNames?.length ? yDimensionsNames.slice(1) : keys
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
normalizeMatrix(data, yDimensionsNames) {
|
|
256
|
+
if (data.length < 2) {
|
|
257
|
+
throw new Error('Minimum data length is 2.');
|
|
258
|
+
}
|
|
259
|
+
if (!yDimensionsNames?.length) {
|
|
260
|
+
throw new Error('Requires yDimensionsNames. e.g. ["v1", "v2", "v3"]');
|
|
261
|
+
}
|
|
262
|
+
const keys = [...yDimensionsNames];
|
|
263
|
+
const tsColumn = keys.shift();
|
|
264
|
+
if (!tsColumn) {
|
|
265
|
+
throw new Error('No time dimension found.');
|
|
266
|
+
}
|
|
267
|
+
const totalCol = data[0]?.length ?? 0;
|
|
268
|
+
if (totalCol !== yDimensionsNames.length) {
|
|
269
|
+
throw new Error(`Dimensions length ${yDimensionsNames.length} does not match total columns ${totalCol}.`);
|
|
270
|
+
}
|
|
271
|
+
const dataset = {
|
|
272
|
+
[tsColumn]: data.map((row) => Number(row[0]))
|
|
273
|
+
};
|
|
274
|
+
keys.forEach((key, index) => {
|
|
275
|
+
dataset[key] = data.map((row) => Number(row[index + 1]));
|
|
276
|
+
});
|
|
277
|
+
return {
|
|
278
|
+
tsColumn,
|
|
279
|
+
dataset,
|
|
280
|
+
yDimensions: keys,
|
|
281
|
+
yDimensionNames: keys
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
createSeriesData() {
|
|
285
|
+
if (!this.yDimensions.length || !this.yDimensionNames.length) {
|
|
286
|
+
throw new Error('No dimensions found.');
|
|
287
|
+
}
|
|
288
|
+
if (this.yDimensions.length !== this.yDimensionNames.length) {
|
|
289
|
+
throw new Error(`Dimensions length ${this.yDimensionNames.length} does not match total columns ${this.yDimensions.length}.`);
|
|
290
|
+
}
|
|
291
|
+
this.syncPriceScales();
|
|
292
|
+
this.yDimensions.forEach((dim, index) => {
|
|
293
|
+
const isSelected = (this.yDimensions.length > 1 && dim === 'price') || this.yDimensions.length === 1;
|
|
294
|
+
this.addSeries(dim, this.yDimensionNames[index], isSelected, index);
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
addSeries(dim, dimName, isSelected, index) {
|
|
298
|
+
const pricePrecision = getPricePrecision(this.dataset[dim] ?? []);
|
|
299
|
+
const series = this.LightweightChart.addSeries(LineSeries, {
|
|
300
|
+
color: SERIES_COLORS[(index ?? this.series.size) % SERIES_COLORS.length],
|
|
301
|
+
lineWidth: 1,
|
|
302
|
+
title: dimName,
|
|
303
|
+
priceScaleId: this.isPercentageDimension(dim) ? 'left' : 'right',
|
|
304
|
+
visible: isSelected,
|
|
305
|
+
crosshairMarkerVisible: false,
|
|
306
|
+
lastValueVisible: true,
|
|
307
|
+
priceFormat: {
|
|
308
|
+
type: 'price',
|
|
309
|
+
precision: pricePrecision,
|
|
310
|
+
minMove: 10 ** -pricePrecision
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
this.series.set(dim, series);
|
|
314
|
+
this.selected[dim] = isSelected;
|
|
315
|
+
this.markersPlugins.set(dim, createSeriesMarkers(series, []));
|
|
316
|
+
}
|
|
317
|
+
syncSeriesMarkers(dimName) {
|
|
318
|
+
const plugin = this.markersPlugins.get(dimName);
|
|
319
|
+
if (!plugin) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
// Lightweight Charts requires unique timestamps per series — duplicate UTCTimestamps
|
|
323
|
+
// within the same second are collapsed, keeping only the first marker at that time.
|
|
324
|
+
const seenTimes = new Set();
|
|
325
|
+
const markers = (this.markers.get(dimName) ?? [])
|
|
326
|
+
.filter((marker) => marker.visible)
|
|
327
|
+
.sort((a, b) => a.time - b.time)
|
|
328
|
+
.filter((marker) => {
|
|
329
|
+
const t = marker.time;
|
|
330
|
+
if (seenTimes.has(t))
|
|
331
|
+
return false;
|
|
332
|
+
seenTimes.add(t);
|
|
333
|
+
return true;
|
|
334
|
+
})
|
|
335
|
+
.map((marker) => ({
|
|
336
|
+
time: marker.time,
|
|
337
|
+
color: marker.color || '#888888',
|
|
338
|
+
shape: marker.shape,
|
|
339
|
+
position: marker.position,
|
|
340
|
+
size: marker.size,
|
|
341
|
+
...(marker.text ? { text: marker.text } : {})
|
|
342
|
+
}));
|
|
343
|
+
plugin.setMarkers(markers);
|
|
344
|
+
if (markers.length > 0) {
|
|
345
|
+
this.expandTimeRangeForMarkers();
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
expandTimeRangeForMarkers() {
|
|
349
|
+
const timeScale = this.LightweightChart.timeScale();
|
|
350
|
+
const visibleRange = timeScale.getVisibleRange();
|
|
351
|
+
if (!visibleRange) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
let minTime = visibleRange.from;
|
|
355
|
+
let maxTime = visibleRange.to;
|
|
356
|
+
for (const [, markerList] of this.markers) {
|
|
357
|
+
for (const m of markerList) {
|
|
358
|
+
const t = m.time;
|
|
359
|
+
if (t < minTime)
|
|
360
|
+
minTime = t;
|
|
361
|
+
if (t > maxTime)
|
|
362
|
+
maxTime = t;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
if (minTime < visibleRange.from || maxTime > visibleRange.to) {
|
|
366
|
+
const padding = (maxTime - minTime) * 0.1;
|
|
367
|
+
timeScale.setVisibleRange({
|
|
368
|
+
from: (minTime - padding),
|
|
369
|
+
to: (maxTime + padding)
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
clearSeries() {
|
|
374
|
+
for (const plugin of this.markersPlugins.values()) {
|
|
375
|
+
plugin.detach();
|
|
376
|
+
}
|
|
377
|
+
for (const series of this.series.values()) {
|
|
378
|
+
this.LightweightChart.removeSeries(series);
|
|
379
|
+
}
|
|
380
|
+
this.series.clear();
|
|
381
|
+
this.markersPlugins.clear();
|
|
382
|
+
this.markers.clear();
|
|
383
|
+
this._timeRangeForced = false;
|
|
384
|
+
this._building = false;
|
|
385
|
+
}
|
|
386
|
+
toLineData(dim) {
|
|
387
|
+
const timestamps = this.dataset[this._tsColumn] ?? [];
|
|
388
|
+
const values = this.dataset[dim] ?? [];
|
|
389
|
+
if (timestamps.length === 0) {
|
|
390
|
+
return [];
|
|
391
|
+
}
|
|
392
|
+
const seen = new Set();
|
|
393
|
+
const result = [];
|
|
394
|
+
for (let i = 0; i < timestamps.length; i++) {
|
|
395
|
+
const t = this.toChartTime(timestamps[i]);
|
|
396
|
+
const tNum = t;
|
|
397
|
+
if (seen.has(tNum))
|
|
398
|
+
continue;
|
|
399
|
+
seen.add(tNum);
|
|
400
|
+
result.push({ time: t, value: values[i] });
|
|
401
|
+
}
|
|
402
|
+
return result;
|
|
403
|
+
}
|
|
404
|
+
toChartTime(timestamp) {
|
|
405
|
+
return Math.floor(timestamp / 1000);
|
|
406
|
+
}
|
|
407
|
+
forceTimeRangeToDataset() {
|
|
408
|
+
if (this._timeRangeForced) {
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
this._timeRangeForced = true;
|
|
412
|
+
const timestamps = this.dataset[this._tsColumn] ?? [];
|
|
413
|
+
if (timestamps.length === 0) {
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
let minMs = timestamps[0];
|
|
417
|
+
let maxMs = timestamps[0];
|
|
418
|
+
for (let i = 1; i < timestamps.length; i++) {
|
|
419
|
+
const t = timestamps[i];
|
|
420
|
+
if (t < minMs)
|
|
421
|
+
minMs = t;
|
|
422
|
+
if (t > maxMs)
|
|
423
|
+
maxMs = t;
|
|
424
|
+
}
|
|
425
|
+
const minSec = this.toChartTime(minMs);
|
|
426
|
+
const maxSec = this.toChartTime(maxMs);
|
|
427
|
+
this.LightweightChart.timeScale().setVisibleRange({
|
|
428
|
+
from: minSec,
|
|
429
|
+
to: maxSec
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
isPercentageDimension(dim) {
|
|
433
|
+
return !dim.startsWith('_') && dim.endsWith('%');
|
|
434
|
+
}
|
|
435
|
+
syncPriceScales() {
|
|
436
|
+
const hasPercentageSeries = this.yDimensions.some((dim) => this.isPercentageDimension(dim));
|
|
437
|
+
const hasValueSeries = this.yDimensions.some((dim) => !this.isPercentageDimension(dim));
|
|
438
|
+
this.LightweightChart.applyOptions({
|
|
439
|
+
leftPriceScale: {
|
|
440
|
+
visible: hasPercentageSeries
|
|
441
|
+
},
|
|
442
|
+
rightPriceScale: {
|
|
443
|
+
visible: hasValueSeries
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
mapMarkerShape(shape) {
|
|
448
|
+
if (shape === 'circle' || shape === 'arrowUp' || shape === 'arrowDown') {
|
|
449
|
+
return shape;
|
|
450
|
+
}
|
|
451
|
+
if (shape === 'none') {
|
|
452
|
+
return 'circle';
|
|
453
|
+
}
|
|
454
|
+
return 'square';
|
|
455
|
+
}
|
|
456
|
+
mapMarkerPosition(position) {
|
|
457
|
+
if (position === 'inside') {
|
|
458
|
+
return 'inBar';
|
|
459
|
+
}
|
|
460
|
+
if (position === 'aboveBar' || position === 'belowBar' || position === 'inBar') {
|
|
461
|
+
return position;
|
|
462
|
+
}
|
|
463
|
+
return 'aboveBar';
|
|
464
|
+
}
|
|
465
|
+
}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import {
|
|
3
|
+
createChart,
|
|
4
|
+
type ChartOptions,
|
|
5
|
+
type DeepPartial,
|
|
6
|
+
type IChartApi,
|
|
7
|
+
type ISeriesApi,
|
|
8
|
+
type SeriesType,
|
|
9
|
+
type MouseEventParams,
|
|
10
|
+
type Time
|
|
11
|
+
} from 'lightweight-charts';
|
|
12
|
+
import { formatPreciseValue } from './LightweightTimeSeriesChartBuilder';
|
|
13
|
+
|
|
14
|
+
type LightweightChartsConfig = {
|
|
15
|
+
options?: DeepPartial<ChartOptions>;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
type TooltipRow = { label: string; value: string };
|
|
19
|
+
|
|
20
|
+
let {
|
|
21
|
+
onLoad,
|
|
22
|
+
config,
|
|
23
|
+
loading = $bindable(false),
|
|
24
|
+
onClear = $bindable(),
|
|
25
|
+
isDark = false
|
|
26
|
+
}: {
|
|
27
|
+
onLoad: (instance: IChartApi) => Promise<void>;
|
|
28
|
+
config?: LightweightChartsConfig;
|
|
29
|
+
loading?: boolean;
|
|
30
|
+
onClear?: () => void;
|
|
31
|
+
isDark?: boolean;
|
|
32
|
+
} = $props();
|
|
33
|
+
|
|
34
|
+
let chart: IChartApi | undefined;
|
|
35
|
+
|
|
36
|
+
// Tooltip state
|
|
37
|
+
let tooltipVisible = $state(false);
|
|
38
|
+
let tooltipX = $state(0);
|
|
39
|
+
let tooltipY = $state(0);
|
|
40
|
+
let tooltipTime = $state('');
|
|
41
|
+
let tooltipRows = $state<TooltipRow[]>([]);
|
|
42
|
+
|
|
43
|
+
const baseOptions = $derived<DeepPartial<ChartOptions>>({
|
|
44
|
+
layout: {
|
|
45
|
+
background: {
|
|
46
|
+
color: isDark ? '#100c2a' : '#ffffff'
|
|
47
|
+
},
|
|
48
|
+
textColor: isDark ? '#f8fafc' : '#0f172a'
|
|
49
|
+
},
|
|
50
|
+
grid: {
|
|
51
|
+
vertLines: {
|
|
52
|
+
color: isDark ? 'rgba(148, 163, 184, 0.12)' : 'rgba(148, 163, 184, 0.2)'
|
|
53
|
+
},
|
|
54
|
+
horzLines: {
|
|
55
|
+
color: isDark ? 'rgba(148, 163, 184, 0.12)' : 'rgba(148, 163, 184, 0.2)'
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
rightPriceScale: {
|
|
59
|
+
borderColor: isDark ? '#334155' : '#cbd5e1'
|
|
60
|
+
},
|
|
61
|
+
leftPriceScale: {
|
|
62
|
+
borderColor: isDark ? '#334155' : '#cbd5e1'
|
|
63
|
+
},
|
|
64
|
+
timeScale: {
|
|
65
|
+
borderColor: isDark ? '#334155' : '#cbd5e1',
|
|
66
|
+
timeVisible: true
|
|
67
|
+
},
|
|
68
|
+
crosshair: {
|
|
69
|
+
vertLine: {
|
|
70
|
+
color: isDark ? '#64748b' : '#94a3b8'
|
|
71
|
+
},
|
|
72
|
+
horzLine: {
|
|
73
|
+
color: isDark ? '#64748b' : '#94a3b8'
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const chartOptions = $derived({
|
|
79
|
+
...baseOptions,
|
|
80
|
+
...(config?.options ?? {})
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
function formatTime(time: Time): string {
|
|
84
|
+
const ts = (time as number) * 1000;
|
|
85
|
+
const d = new Date(ts);
|
|
86
|
+
return d.toISOString().replace('T', ' ').slice(0, 19);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function handleCrosshairMove(param: MouseEventParams<Time>, container: HTMLElement) {
|
|
90
|
+
if (!param.point || !param.time || param.seriesData.size === 0) {
|
|
91
|
+
tooltipVisible = false;
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const rows: TooltipRow[] = [];
|
|
96
|
+
for (const [series, data] of param.seriesData as Map<
|
|
97
|
+
ISeriesApi<SeriesType, Time>,
|
|
98
|
+
{ value?: number }
|
|
99
|
+
>) {
|
|
100
|
+
if (!series.options().visible) continue;
|
|
101
|
+
const value = data?.value;
|
|
102
|
+
if (value === undefined || value === null) continue;
|
|
103
|
+
const label = (series.options() as { title?: string }).title ?? '';
|
|
104
|
+
rows.push({ label, value: formatPreciseValue(value) });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (rows.length === 0) {
|
|
108
|
+
tooltipVisible = false;
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
tooltipRows = rows;
|
|
113
|
+
tooltipTime = formatTime(param.time);
|
|
114
|
+
|
|
115
|
+
// Position tooltip near cursor, avoiding overflow
|
|
116
|
+
const containerRect = container.getBoundingClientRect();
|
|
117
|
+
const TOOLTIP_WIDTH = 160;
|
|
118
|
+
const TOOLTIP_OFFSET = 12;
|
|
119
|
+
|
|
120
|
+
let x = param.point.x + TOOLTIP_OFFSET;
|
|
121
|
+
if (x + TOOLTIP_WIDTH > containerRect.width) {
|
|
122
|
+
x = param.point.x - TOOLTIP_WIDTH - TOOLTIP_OFFSET;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
tooltipX = x;
|
|
126
|
+
tooltipY = Math.max(0, param.point.y - 20);
|
|
127
|
+
tooltipVisible = true;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function chartAction(element: HTMLElement) {
|
|
131
|
+
chart = createChart(element, chartOptions);
|
|
132
|
+
|
|
133
|
+
const handleResize = () => {
|
|
134
|
+
chart?.applyOptions({
|
|
135
|
+
width: element.clientWidth,
|
|
136
|
+
height: element.clientHeight
|
|
137
|
+
});
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const resizeObserver =
|
|
141
|
+
typeof ResizeObserver === 'undefined' ? undefined : new ResizeObserver(handleResize);
|
|
142
|
+
|
|
143
|
+
if (resizeObserver) {
|
|
144
|
+
resizeObserver.observe(element);
|
|
145
|
+
} else {
|
|
146
|
+
window.addEventListener('resize', handleResize);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const onCrosshairMove = (param: MouseEventParams<Time>) => handleCrosshairMove(param, element);
|
|
150
|
+
chart.subscribeCrosshairMove(onCrosshairMove);
|
|
151
|
+
|
|
152
|
+
handleResize();
|
|
153
|
+
onClear = () => undefined;
|
|
154
|
+
onLoad(chart);
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
destroy() {
|
|
158
|
+
chart?.unsubscribeCrosshairMove(onCrosshairMove);
|
|
159
|
+
if (resizeObserver) {
|
|
160
|
+
resizeObserver.disconnect();
|
|
161
|
+
} else {
|
|
162
|
+
window.removeEventListener('resize', handleResize);
|
|
163
|
+
}
|
|
164
|
+
chart?.remove();
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
$effect(() => {
|
|
170
|
+
chart?.applyOptions(chartOptions);
|
|
171
|
+
});
|
|
172
|
+
</script>
|
|
173
|
+
|
|
174
|
+
<div style="position: relative; width: 100%; height: 100%;">
|
|
175
|
+
<div class="lightweight-charts" use:chartAction></div>
|
|
176
|
+
|
|
177
|
+
{#if tooltipVisible}
|
|
178
|
+
<div
|
|
179
|
+
class="lw-tooltip"
|
|
180
|
+
class:lw-tooltip--dark={isDark}
|
|
181
|
+
style="left: {tooltipX}px; top: {tooltipY}px;"
|
|
182
|
+
>
|
|
183
|
+
<div class="lw-tooltip__time">{tooltipTime}</div>
|
|
184
|
+
{#each tooltipRows as row (row.label)}
|
|
185
|
+
<div class="lw-tooltip__row">
|
|
186
|
+
{#if row.label}<span class="lw-tooltip__label">{row.label}</span>{/if}
|
|
187
|
+
<span class="lw-tooltip__value">{row.value}</span>
|
|
188
|
+
</div>
|
|
189
|
+
{/each}
|
|
190
|
+
</div>
|
|
191
|
+
{/if}
|
|
192
|
+
</div>
|
|
193
|
+
|
|
194
|
+
<style>
|
|
195
|
+
.lightweight-charts {
|
|
196
|
+
width: 100%;
|
|
197
|
+
height: 100%;
|
|
198
|
+
min-height: 100px;
|
|
199
|
+
position: relative;
|
|
200
|
+
z-index: 1;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.lw-tooltip {
|
|
204
|
+
position: absolute;
|
|
205
|
+
z-index: 10;
|
|
206
|
+
pointer-events: none;
|
|
207
|
+
background: rgba(255, 255, 255, 0.95);
|
|
208
|
+
border: 1px solid #cbd5e1;
|
|
209
|
+
border-radius: 6px;
|
|
210
|
+
padding: 6px 10px;
|
|
211
|
+
font-size: 12px;
|
|
212
|
+
color: #0f172a;
|
|
213
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
|
|
214
|
+
min-width: 120px;
|
|
215
|
+
max-width: 200px;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.lw-tooltip--dark {
|
|
219
|
+
background: rgba(20, 16, 50, 0.95);
|
|
220
|
+
border-color: #334155;
|
|
221
|
+
color: #f8fafc;
|
|
222
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.lw-tooltip__time {
|
|
226
|
+
font-size: 11px;
|
|
227
|
+
opacity: 0.6;
|
|
228
|
+
margin-bottom: 4px;
|
|
229
|
+
white-space: nowrap;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.lw-tooltip__row {
|
|
233
|
+
display: flex;
|
|
234
|
+
justify-content: space-between;
|
|
235
|
+
gap: 8px;
|
|
236
|
+
line-height: 1.6;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.lw-tooltip__label {
|
|
240
|
+
opacity: 0.75;
|
|
241
|
+
overflow: hidden;
|
|
242
|
+
text-overflow: ellipsis;
|
|
243
|
+
white-space: nowrap;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.lw-tooltip__value {
|
|
247
|
+
font-weight: 600;
|
|
248
|
+
white-space: nowrap;
|
|
249
|
+
}
|
|
250
|
+
</style>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type ChartOptions, type DeepPartial, type IChartApi } from 'lightweight-charts';
|
|
2
|
+
type LightweightChartsConfig = {
|
|
3
|
+
options?: DeepPartial<ChartOptions>;
|
|
4
|
+
};
|
|
5
|
+
type $$ComponentProps = {
|
|
6
|
+
onLoad: (instance: IChartApi) => Promise<void>;
|
|
7
|
+
config?: LightweightChartsConfig;
|
|
8
|
+
loading?: boolean;
|
|
9
|
+
onClear?: () => void;
|
|
10
|
+
isDark?: boolean;
|
|
11
|
+
};
|
|
12
|
+
declare const SVELightweightCharts: import("svelte").Component<$$ComponentProps, {}, "loading" | "onClear">;
|
|
13
|
+
type SVELightweightCharts = ReturnType<typeof SVELightweightCharts>;
|
|
14
|
+
export default SVELightweightCharts;
|
|
15
|
+
//# sourceMappingURL=SVELightweightCharts.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SVELightweightCharts.svelte.d.ts","sourceRoot":"","sources":["../src/lib/SVELightweightCharts.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,SAAS,EAKd,MAAM,oBAAoB,CAAC;AAI5B,KAAK,uBAAuB,GAAG;IAC9B,OAAO,CAAC,EAAE,WAAW,CAAC,YAAY,CAAC,CAAC;CACpC,CAAC;AAGF,KAAK,gBAAgB,GAAI;IACxB,MAAM,EAAE,CAAC,QAAQ,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,MAAM,CAAC,EAAE,uBAAuB,CAAC;IACjC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AA+KH,QAAA,MAAM,oBAAoB,yEAAwC,CAAC;AACnE,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC;AACpE,eAAe,oBAAoB,CAAC"}
|
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
import { type LineSeriesOption, type DataZoomComponentOption } from 'echarts';
|
|
2
2
|
import { type ECharts } from './';
|
|
3
|
+
import type { ChartMarkerPointOptions } from './chartAdapter';
|
|
3
4
|
import type { GridOption } from 'echarts/types/dist/shared';
|
|
4
5
|
import type { ZRColor } from 'echarts/types/src/util/types.js';
|
|
5
6
|
type IconType = 'circle' | 'rect' | 'roundRect' | 'triangle' | 'diamond' | 'pin' | 'arrowUp' | 'arrowDown' | 'none';
|
|
6
|
-
type LabelPosition = 'top' | 'left' | 'right' | 'bottom' | 'inside' | 'insideLeft' | 'insideRight' | 'insideTop' | 'insideBottom' | 'insideTopLeft' | 'insideBottomLeft' | 'insideTopRight' | 'insideBottomRight';
|
|
7
|
-
type MarkerPointOption = {
|
|
8
|
-
icon: IconType;
|
|
9
|
-
color: ZRColor;
|
|
10
|
-
position: LabelPosition;
|
|
11
|
-
symbolSize: number;
|
|
12
|
-
};
|
|
13
7
|
type DatasetFormatSimpleObject = Record<string, number[]>;
|
|
14
8
|
type DatasetFormatObject = Record<string, any>[];
|
|
15
9
|
type DatasetFormatArray = number[][];
|
|
@@ -49,6 +43,8 @@ export declare class TimeSeriesChartBuilder {
|
|
|
49
43
|
setDataset(data: DatasetFormatArray | DatasetFormatObject | DatasetFormatSimpleObject, yDimensionsNames?: string[]): this;
|
|
50
44
|
toggleLegend(column: string): this;
|
|
51
45
|
goToZoom(start: number, end: number): this;
|
|
46
|
+
scrollToTime(timestamp: number): this;
|
|
47
|
+
private findClosestDataIndex;
|
|
52
48
|
/**
|
|
53
49
|
* data: [1658870400, 823, 95.8, ...]
|
|
54
50
|
* dimensionsNames: ['_ts', 'price', 'otherColumn', ...]
|
|
@@ -100,7 +96,7 @@ export declare class TimeSeriesChartBuilder {
|
|
|
100
96
|
dimName: string;
|
|
101
97
|
timestamp: number;
|
|
102
98
|
name?: string;
|
|
103
|
-
}, options?:
|
|
99
|
+
}, options?: ChartMarkerPointOptions): this;
|
|
104
100
|
private isNumberArray;
|
|
105
101
|
/**
|
|
106
102
|
* Creates the series data
|
|
@@ -124,8 +120,9 @@ export declare class TimeSeriesChartBuilder {
|
|
|
124
120
|
private isSimpleObject;
|
|
125
121
|
private isRecordArray;
|
|
126
122
|
private isNumberMatrix;
|
|
127
|
-
getRangeValues():
|
|
128
|
-
toggleMarkers(id: number, dimName: string, shape: string): this
|
|
123
|
+
getRangeValues(): [number, number];
|
|
124
|
+
toggleMarkers(id: number, dimName: string, shape: string): this;
|
|
125
|
+
clearMarkers(): this;
|
|
129
126
|
}
|
|
130
127
|
export {};
|
|
131
128
|
//# sourceMappingURL=TimeSeriesChartBuilder.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimeSeriesChartBuilder.d.ts","sourceRoot":"","sources":["../src/lib/TimeSeriesChartBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,KAAK,gBAAgB,EACrB,KAAK,uBAAuB,EAE5B,MAAM,SAAS,CAAC;AACjB,OAAO,EAAsB,KAAK,OAAO,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"TimeSeriesChartBuilder.d.ts","sourceRoot":"","sources":["../src/lib/TimeSeriesChartBuilder.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,6 +12,7 @@ export class TimeSeriesChartBuilder {
|
|
|
12
12
|
constructor(instance, builderConfig) {
|
|
13
13
|
this.ECharts = instance;
|
|
14
14
|
this.builderConfig = { ...this.builderConfig, ...builderConfig };
|
|
15
|
+
this.option.useUTC = true;
|
|
15
16
|
this.option.animation = false;
|
|
16
17
|
this.option.legend = this.builderConfig.externalManagerLegend
|
|
17
18
|
? {
|
|
@@ -56,7 +57,13 @@ export class TimeSeriesChartBuilder {
|
|
|
56
57
|
};
|
|
57
58
|
this.option.xAxis = {
|
|
58
59
|
type: 'time',
|
|
59
|
-
axisLine: { show: true }
|
|
60
|
+
axisLine: { show: true },
|
|
61
|
+
axisLabel: {
|
|
62
|
+
formatter: (value) => {
|
|
63
|
+
const d = new Date(value);
|
|
64
|
+
return d.toTimeString().slice(0, 8);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
60
67
|
};
|
|
61
68
|
this.option.yAxis = [
|
|
62
69
|
{
|
|
@@ -129,6 +136,45 @@ export class TimeSeriesChartBuilder {
|
|
|
129
136
|
});
|
|
130
137
|
return this;
|
|
131
138
|
}
|
|
139
|
+
scrollToTime(timestamp) {
|
|
140
|
+
const [min, max] = this.getRangeValues();
|
|
141
|
+
if (!min || !max)
|
|
142
|
+
return this;
|
|
143
|
+
const range = max - min;
|
|
144
|
+
const targetPercent = ((timestamp - min) / range) * 100;
|
|
145
|
+
const windowSize = 5;
|
|
146
|
+
const start = Math.max(0, targetPercent - windowSize / 2);
|
|
147
|
+
const end = Math.min(100, targetPercent + windowSize / 2);
|
|
148
|
+
this.ECharts.dispatchAction({
|
|
149
|
+
type: 'dataZoom',
|
|
150
|
+
start,
|
|
151
|
+
end
|
|
152
|
+
});
|
|
153
|
+
setTimeout(() => {
|
|
154
|
+
this.ECharts.dispatchAction({
|
|
155
|
+
type: 'showTip',
|
|
156
|
+
seriesIndex: 0,
|
|
157
|
+
dataIndex: this.findClosestDataIndex(timestamp)
|
|
158
|
+
});
|
|
159
|
+
}, 100);
|
|
160
|
+
return this;
|
|
161
|
+
}
|
|
162
|
+
findClosestDataIndex(timestamp) {
|
|
163
|
+
const dataset = this.option.dataset;
|
|
164
|
+
const ts = dataset?.source?.[this._tsColumn] ?? [];
|
|
165
|
+
if (!ts.length)
|
|
166
|
+
return 0;
|
|
167
|
+
let closest = 0;
|
|
168
|
+
let minDiff = Math.abs(ts[0] - timestamp);
|
|
169
|
+
for (let i = 1; i < ts.length; i++) {
|
|
170
|
+
const diff = Math.abs(ts[i] - timestamp);
|
|
171
|
+
if (diff < minDiff) {
|
|
172
|
+
minDiff = diff;
|
|
173
|
+
closest = i;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return closest;
|
|
177
|
+
}
|
|
132
178
|
/**
|
|
133
179
|
* data: [1658870400, 823, 95.8, ...]
|
|
134
180
|
* dimensionsNames: ['_ts', 'price', 'otherColumn', ...]
|
|
@@ -547,7 +593,6 @@ export class TimeSeriesChartBuilder {
|
|
|
547
593
|
if (this.yDimensions.length !== this.yDimensionNames.length) {
|
|
548
594
|
throw new Error(`Dimensions length ${this.yDimensionNames.length} does not match total columns ${this.yDimensions.length}.`);
|
|
549
595
|
}
|
|
550
|
-
this.option.xAxis = { type: 'time', name: timeDimensionName };
|
|
551
596
|
this.yDimensions.map((dim, inx) => this.addSeries(dim, this.yDimensionNames[inx], (this.yDimensions.length > 1 && dim === 'price') || this.yDimensions.length === 1));
|
|
552
597
|
}
|
|
553
598
|
/**
|
|
@@ -690,10 +735,22 @@ export class TimeSeriesChartBuilder {
|
|
|
690
735
|
const markerPoints = seriesDimension?.markPoint.data;
|
|
691
736
|
const point = markerPoints.find((mp) => mp.name === `markerpoint-${id}`);
|
|
692
737
|
if (!point) {
|
|
693
|
-
return;
|
|
738
|
+
return this;
|
|
694
739
|
}
|
|
695
740
|
point.symbol = point.symbol === 'none' ? this.getIcon(shape) : 'none';
|
|
696
741
|
this.build();
|
|
697
742
|
return this;
|
|
698
743
|
}
|
|
744
|
+
clearMarkers() {
|
|
745
|
+
if (!Array.isArray(this.option.series)) {
|
|
746
|
+
return this;
|
|
747
|
+
}
|
|
748
|
+
for (const s of this.option.series) {
|
|
749
|
+
if (s.markPoint) {
|
|
750
|
+
s.markPoint.data = [];
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
this.build();
|
|
754
|
+
return this;
|
|
755
|
+
}
|
|
699
756
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export type ChartDatasetFormatSimpleObject = Record<string, number[]>;
|
|
2
|
+
export type ChartDatasetFormatObject = Record<string, any>[];
|
|
3
|
+
export type ChartDatasetFormatArray = number[][];
|
|
4
|
+
export type ChartDataset = ChartDatasetFormatArray | ChartDatasetFormatObject | ChartDatasetFormatSimpleObject;
|
|
5
|
+
export type ChartMarkerPoint = {
|
|
6
|
+
dimName: string;
|
|
7
|
+
timestamp: number;
|
|
8
|
+
name?: string;
|
|
9
|
+
};
|
|
10
|
+
export type ChartMarkerPointOptions = {
|
|
11
|
+
icon?: string;
|
|
12
|
+
color?: string;
|
|
13
|
+
position?: string;
|
|
14
|
+
symbolSize?: number;
|
|
15
|
+
};
|
|
16
|
+
export interface TimeSeriesChartAdapter {
|
|
17
|
+
setLegendIcon(icon: string): this;
|
|
18
|
+
setDataset(data: ChartDataset, yDimensionsNames?: string[]): this;
|
|
19
|
+
build(): this;
|
|
20
|
+
addDimension(data: ChartDatasetFormatSimpleObject, dimName: string): this;
|
|
21
|
+
addMarkerPoint(id: number, data: ChartMarkerPoint, options?: ChartMarkerPointOptions): this;
|
|
22
|
+
getLegendStatus(): Record<string, boolean>;
|
|
23
|
+
toggleLegend(column: string): this;
|
|
24
|
+
getRangeValues(): [number, number];
|
|
25
|
+
goToZoom(start: number, end: number): this;
|
|
26
|
+
scrollToTime(timestamp: number): this;
|
|
27
|
+
getTotalRows(): number;
|
|
28
|
+
toggleMarkers(id: number, dimName: string, shape: string): this;
|
|
29
|
+
clearMarkers(): this;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=chartAdapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chartAdapter.d.ts","sourceRoot":"","sources":["../src/lib/chartAdapter.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,8BAA8B,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;AACtE,MAAM,MAAM,wBAAwB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;AAC7D,MAAM,MAAM,uBAAuB,GAAG,MAAM,EAAE,EAAE,CAAC;AAEjD,MAAM,MAAM,YAAY,GACrB,uBAAuB,GACvB,wBAAwB,GACxB,8BAA8B,CAAC;AAElC,MAAM,MAAM,gBAAgB,GAAG;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,WAAW,sBAAsB;IACtC,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,UAAU,CAAC,IAAI,EAAE,YAAY,EAAE,gBAAgB,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAClE,KAAK,IAAI,IAAI,CAAC;IACd,YAAY,CAAC,IAAI,EAAE,8BAA8B,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1E,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,CAAC,EAAE,uBAAuB,GAAG,IAAI,CAAC;IAC5F,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3C,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,cAAc,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,YAAY,IAAI,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAChE,YAAY,IAAI,IAAI,CAAC;CACrB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export { default as SVECharts } from './SVECharts.svelte';
|
|
2
|
+
export { default as SVELightweightCharts } from './SVELightweightCharts.svelte';
|
|
3
|
+
export type { TimeSeriesChartAdapter } from './chartAdapter';
|
|
4
|
+
export { LightweightTimeSeriesChartBuilder } from './LightweightTimeSeriesChartBuilder';
|
|
2
5
|
export { TimeSeriesChartBuilder } from './TimeSeriesChartBuilder';
|
|
3
6
|
export * from './types';
|
|
4
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,cAAc,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAChF,YAAY,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAC7D,OAAO,EAAE,iCAAiC,EAAE,MAAM,qCAAqC,CAAC;AACxF,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,cAAc,SAAS,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { default as SVECharts } from './SVECharts.svelte';
|
|
2
|
+
export { default as SVELightweightCharts } from './SVELightweightCharts.svelte';
|
|
3
|
+
export { LightweightTimeSeriesChartBuilder } from './LightweightTimeSeriesChartBuilder';
|
|
2
4
|
export { TimeSeriesChartBuilder } from './TimeSeriesChartBuilder';
|
|
3
5
|
export * from './types';
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export type { DataRange, DataZoomEvent } from './SVECharts.svelte';
|
|
2
2
|
export type { ECharts } from 'echarts/types/dist/core';
|
|
3
3
|
export type { EChartsOption } from 'echarts';
|
|
4
|
+
export type { IChartApi as LightweightChartApi, Time as LightweightChartTime } from 'lightweight-charts';
|
|
4
5
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/lib/types.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnE,YAAY,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AACvD,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/lib/types.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnE,YAAY,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AACvD,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7C,YAAY,EACX,SAAS,IAAI,mBAAmB,EAChC,IAAI,IAAI,oBAAoB,EAC5B,MAAM,oBAAoB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"private": false,
|
|
3
3
|
"name": "@qtsurfer/sveltecharts",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.7.0",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "QTSurfer (https://github.com/QTSurfer)",
|
|
7
7
|
"homepage": "https://github.com/QTSurfer/svelte-timeseries/tree/main/packages/sveltecharts",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"types": "./dist/index.d.ts",
|
|
29
29
|
"peerDependencies": {
|
|
30
30
|
"echarts": "^6.0.0",
|
|
31
|
+
"lightweight-charts": "^5.1.0",
|
|
31
32
|
"svelte": "^5.43.14"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
@@ -52,6 +53,7 @@
|
|
|
52
53
|
"publint": "^0.3.18",
|
|
53
54
|
"svelte": "^5.55.0",
|
|
54
55
|
"svelte-check": "^4.4.5",
|
|
56
|
+
"svelte-eslint-parser": "^1.6.0",
|
|
55
57
|
"typescript": "^5.9.3",
|
|
56
58
|
"vite": "^7.2.4",
|
|
57
59
|
"vitest": "^4.1.0"
|