@qtsurfer/sveltecharts 0.2.12 → 0.4.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/SVECharts.svelte +148 -78
- package/dist/SVECharts.svelte.d.ts +21 -35
- package/dist/SVECharts.svelte.d.ts.map +1 -1
- package/dist/TimeSeriesChartBuilder.d.ts +131 -0
- package/dist/TimeSeriesChartBuilder.d.ts.map +1 -0
- package/dist/TimeSeriesChartBuilder.js +699 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/mockDataSet.d.ts +5 -0
- package/dist/mockDataSet.d.ts.map +1 -0
- package/dist/mockDataSet.js +32 -0
- package/package.json +46 -46
package/dist/SVECharts.svelte
CHANGED
|
@@ -1,88 +1,158 @@
|
|
|
1
|
-
<script
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import { init, use } from 'echarts/core';
|
|
3
|
+
import { LineChart, BarChart } from 'echarts/charts';
|
|
4
|
+
import {
|
|
5
|
+
DataZoomComponent,
|
|
6
|
+
LegendComponent,
|
|
7
|
+
TitleComponent,
|
|
8
|
+
TooltipComponent,
|
|
9
|
+
GridComponent,
|
|
10
|
+
DatasetComponent,
|
|
11
|
+
MarkLineComponent,
|
|
12
|
+
MarkPointComponent,
|
|
13
|
+
MarkAreaComponent
|
|
14
|
+
} from 'echarts/components';
|
|
15
|
+
import { LabelLayout } from 'echarts/features';
|
|
16
|
+
import { CanvasRenderer } from 'echarts/renderers';
|
|
17
|
+
import type { ECharts, EChartsOption } from './types';
|
|
18
|
+
import 'echarts/theme/dark.js';
|
|
19
|
+
|
|
20
|
+
// Register the required components
|
|
21
|
+
use([
|
|
22
|
+
LineChart,
|
|
23
|
+
BarChart,
|
|
24
|
+
DataZoomComponent,
|
|
25
|
+
LegendComponent,
|
|
26
|
+
TitleComponent,
|
|
27
|
+
TooltipComponent,
|
|
28
|
+
GridComponent,
|
|
29
|
+
DatasetComponent,
|
|
30
|
+
LabelLayout,
|
|
31
|
+
CanvasRenderer,
|
|
32
|
+
MarkLineComponent,
|
|
33
|
+
MarkPointComponent,
|
|
34
|
+
MarkAreaComponent
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
export type EChartsTheme = string | object;
|
|
38
|
+
export type EChartsRenderer = 'canvas' | 'svg';
|
|
39
|
+
|
|
40
|
+
export type EChartsConfig = {
|
|
41
|
+
theme?: EChartsTheme;
|
|
42
|
+
renderer?: EChartsRenderer;
|
|
43
|
+
option: EChartsOption;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export type DataRange = { start: number; end: number };
|
|
47
|
+
export type DataZoomEventSingle = { batch?: never } & DataRange;
|
|
48
|
+
export type DataZoomEventBatch = { batch: DataRange[]; start?: never; end?: never };
|
|
49
|
+
|
|
50
|
+
export type DataZoomEvent = DataZoomEventBatch | DataZoomEventSingle;
|
|
51
|
+
|
|
52
|
+
const DEFAULT_CONFIG: EChartsConfig = {
|
|
53
|
+
theme: undefined,
|
|
54
|
+
renderer: 'canvas',
|
|
55
|
+
option: {}
|
|
56
|
+
};
|
|
28
57
|
</script>
|
|
29
58
|
|
|
30
|
-
<script
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
59
|
+
<script lang="ts">
|
|
60
|
+
let {
|
|
61
|
+
onLoad,
|
|
62
|
+
config,
|
|
63
|
+
onDataZoom,
|
|
64
|
+
loading = $bindable(false),
|
|
65
|
+
onClear = $bindable(),
|
|
66
|
+
isDark = false
|
|
67
|
+
}: {
|
|
68
|
+
onLoad: (instance: ECharts) => Promise<void>;
|
|
69
|
+
config?: Partial<EChartsConfig>;
|
|
70
|
+
onDataZoom?: (event: DataZoomEventSingle) => void;
|
|
71
|
+
loading?: boolean;
|
|
72
|
+
onClear?: () => void;
|
|
73
|
+
isDark?: boolean;
|
|
74
|
+
} = $props();
|
|
75
|
+
|
|
76
|
+
let instance: ECharts;
|
|
77
|
+
|
|
78
|
+
const { theme, renderer, option } = {
|
|
79
|
+
...DEFAULT_CONFIG,
|
|
80
|
+
...config
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const handleDataZoom = (zoomEvent: unknown) => {
|
|
84
|
+
if (!onDataZoom) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const event = zoomEvent as DataZoomEvent;
|
|
89
|
+
let start: number, end: number;
|
|
90
|
+
|
|
91
|
+
if (event.batch) {
|
|
92
|
+
const [info] = event.batch;
|
|
93
|
+
start = info.start;
|
|
94
|
+
end = info.end;
|
|
95
|
+
} else {
|
|
96
|
+
start = event.start;
|
|
97
|
+
end = event.end;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
onDataZoom({ start, end });
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
function chartAction(element: HTMLElement) {
|
|
104
|
+
instance = init(element, isDark ? 'dark' : undefined, { renderer });
|
|
105
|
+
|
|
106
|
+
const handleResize = () => {
|
|
107
|
+
instance.resize();
|
|
108
|
+
};
|
|
109
|
+
window.addEventListener('resize', handleResize);
|
|
110
|
+
instance.on('datazoom', handleDataZoom);
|
|
111
|
+
|
|
112
|
+
if (Object.keys(option).length) {
|
|
113
|
+
instance.setOption(option);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
onClear = () => instance.clear();
|
|
117
|
+
|
|
118
|
+
onLoad(instance);
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
destroy() {
|
|
122
|
+
instance.off('datazoom', handleDataZoom);
|
|
123
|
+
window.removeEventListener('resize', handleResize);
|
|
124
|
+
instance.dispose();
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* @todo
|
|
128
|
+
* Limiting option assignment. Review implementation
|
|
129
|
+
*/
|
|
130
|
+
// update(config: EChartsConfig) {
|
|
131
|
+
// instance.setOption({
|
|
132
|
+
// ...echartsConfig.option,
|
|
133
|
+
// ...config.option
|
|
134
|
+
// });
|
|
135
|
+
// }
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
$effect(() => {
|
|
140
|
+
if (instance) {
|
|
141
|
+
instance.setTheme({ backgroundColor: isDark ? '#100c2a' : '#fff' });
|
|
142
|
+
}
|
|
143
|
+
});
|
|
79
144
|
</script>
|
|
80
145
|
|
|
81
|
-
<div
|
|
146
|
+
<div style="position: relative; width: 100%; height: 100%;">
|
|
147
|
+
<div id="chart" class="echarts" use:chartAction></div>
|
|
148
|
+
</div>
|
|
82
149
|
|
|
83
150
|
<style>
|
|
84
151
|
.echarts {
|
|
85
152
|
width: 100%;
|
|
86
|
-
height:
|
|
153
|
+
height: 100%;
|
|
154
|
+
min-height: 300px;
|
|
155
|
+
position: relative;
|
|
156
|
+
z-index: 1;
|
|
87
157
|
}
|
|
88
158
|
</style>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import type { ECharts, EChartsOption } from './types';
|
|
2
|
+
import 'echarts/theme/dark.js';
|
|
3
3
|
export type EChartsTheme = string | object;
|
|
4
4
|
export type EChartsRenderer = 'canvas' | 'svg';
|
|
5
5
|
export type EChartsConfig = {
|
|
@@ -11,38 +11,24 @@ export type DataRange = {
|
|
|
11
11
|
start: number;
|
|
12
12
|
end: number;
|
|
13
13
|
};
|
|
14
|
-
export type
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
destroy(): void;
|
|
22
|
-
update(config: EChartsConfig): void;
|
|
23
|
-
};
|
|
24
|
-
showLoading?: (text?: string) => void;
|
|
25
|
-
hideLoading?: () => void;
|
|
26
|
-
};
|
|
27
|
-
events: {
|
|
28
|
-
datazoom: CustomEvent<any>;
|
|
29
|
-
} & {
|
|
30
|
-
[evt: string]: CustomEvent<any>;
|
|
31
|
-
};
|
|
32
|
-
slots: {};
|
|
33
|
-
exports?: {} | undefined;
|
|
34
|
-
bindings?: string | undefined;
|
|
14
|
+
export type DataZoomEventSingle = {
|
|
15
|
+
batch?: never;
|
|
16
|
+
} & DataRange;
|
|
17
|
+
export type DataZoomEventBatch = {
|
|
18
|
+
batch: DataRange[];
|
|
19
|
+
start?: never;
|
|
20
|
+
end?: never;
|
|
35
21
|
};
|
|
36
|
-
export type
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
export
|
|
22
|
+
export type DataZoomEvent = DataZoomEventBatch | DataZoomEventSingle;
|
|
23
|
+
type $$ComponentProps = {
|
|
24
|
+
onLoad: (instance: ECharts) => Promise<void>;
|
|
25
|
+
config?: Partial<EChartsConfig>;
|
|
26
|
+
onDataZoom?: (event: DataZoomEventSingle) => void;
|
|
27
|
+
loading?: boolean;
|
|
28
|
+
onClear?: () => void;
|
|
29
|
+
isDark?: boolean;
|
|
30
|
+
};
|
|
31
|
+
declare const SVECharts: import("svelte").Component<$$ComponentProps, {}, "loading" | "onClear">;
|
|
32
|
+
type SVECharts = ReturnType<typeof SVECharts>;
|
|
33
|
+
export default SVECharts;
|
|
48
34
|
//# sourceMappingURL=SVECharts.svelte.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SVECharts.svelte.d.ts","sourceRoot":"","sources":["../src/lib/SVECharts.svelte.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SVECharts.svelte.d.ts","sourceRoot":"","sources":["../src/lib/SVECharts.svelte.ts"],"names":[],"mappings":"AAkBC,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,uBAAuB,CAAC;AAmB/B,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,CAAC;AAC3C,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,KAAK,CAAC;AAE/C,MAAM,MAAM,aAAa,GAAG;IAC3B,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,MAAM,EAAE,aAAa,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AACvD,MAAM,MAAM,mBAAmB,GAAG;IAAE,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,GAAG,SAAS,CAAC;AAChE,MAAM,MAAM,kBAAkB,GAAG;IAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAC;IAAC,GAAG,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC;AAEpF,MAAM,MAAM,aAAa,GAAG,kBAAkB,GAAG,mBAAmB,CAAC;AAQrE,KAAK,gBAAgB,GAAI;IACxB,MAAM,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IAChC,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAClD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AA2FH,QAAA,MAAM,SAAS,yEAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { type LineSeriesOption, type DataZoomComponentOption } from 'echarts';
|
|
2
|
+
import { type ECharts } from './';
|
|
3
|
+
import type { GridOption } from 'echarts/types/dist/shared';
|
|
4
|
+
import type { ZRColor } from 'echarts/types/src/util/types.js';
|
|
5
|
+
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
|
+
type DatasetFormatSimpleObject = Record<string, number[]>;
|
|
14
|
+
type DatasetFormatObject = Record<string, any>[];
|
|
15
|
+
type DatasetFormatArray = number[][];
|
|
16
|
+
/**
|
|
17
|
+
*
|
|
18
|
+
* ConfigBuilder:
|
|
19
|
+
* externalManagerLegend: Hides EChart legends to allow external management
|
|
20
|
+
*
|
|
21
|
+
*/
|
|
22
|
+
type ConfigBuilder = {
|
|
23
|
+
externalManagerLegend?: boolean;
|
|
24
|
+
};
|
|
25
|
+
export type MarkerEvent = {
|
|
26
|
+
name?: string;
|
|
27
|
+
xAxis: number[];
|
|
28
|
+
icon?: IconType;
|
|
29
|
+
color?: ZRColor;
|
|
30
|
+
position?: 'aboveBar' | 'belowBar';
|
|
31
|
+
};
|
|
32
|
+
export type MarkArea = {
|
|
33
|
+
name?: string;
|
|
34
|
+
xAxis: [number, number];
|
|
35
|
+
color?: ZRColor;
|
|
36
|
+
};
|
|
37
|
+
export declare class TimeSeriesChartBuilder {
|
|
38
|
+
ECharts: ECharts;
|
|
39
|
+
private builderConfig;
|
|
40
|
+
private option;
|
|
41
|
+
private yDimensions;
|
|
42
|
+
private yDimensionNames?;
|
|
43
|
+
private _tsColumn;
|
|
44
|
+
constructor(instance: ECharts, builderConfig?: ConfigBuilder);
|
|
45
|
+
/**
|
|
46
|
+
* Accepts data as rows: [timestamp, v1, v2, ...]
|
|
47
|
+
* Automatically generates line series for each value column (>=1).
|
|
48
|
+
*/
|
|
49
|
+
setDataset(data: DatasetFormatArray | DatasetFormatObject | DatasetFormatSimpleObject, yDimensionsNames?: string[]): this;
|
|
50
|
+
toggleLegend(column: string): this;
|
|
51
|
+
goToZoom(start: number, end: number): this;
|
|
52
|
+
/**
|
|
53
|
+
* data: [1658870400, 823, 95.8, ...]
|
|
54
|
+
* dimensionsNames: ['_ts', 'price', 'otherColumn', ...]
|
|
55
|
+
*/
|
|
56
|
+
private setDatasetByArray;
|
|
57
|
+
/**
|
|
58
|
+
* Data is an array of objects.
|
|
59
|
+
* [
|
|
60
|
+
* {_ts: 1658870400, price: 823, otherColumn: 95.8},
|
|
61
|
+
* {...}
|
|
62
|
+
* ]
|
|
63
|
+
*/
|
|
64
|
+
private setDataByObject;
|
|
65
|
+
private setDataByObjectSimple;
|
|
66
|
+
addDimension(data: DatasetFormatSimpleObject, dimName: string): this;
|
|
67
|
+
private getColumnsSelected;
|
|
68
|
+
addSeries(dim: string, dimName: string, isSelected: boolean): void;
|
|
69
|
+
/**
|
|
70
|
+
* Tooltip bound to axis with a crosshair pointer.
|
|
71
|
+
*/
|
|
72
|
+
setAxisTooltip(): this;
|
|
73
|
+
/**
|
|
74
|
+
* Legend with a custom icon (e.g., 'circle', 'rect').
|
|
75
|
+
*/
|
|
76
|
+
setLegendIcon(icon: IconType): this;
|
|
77
|
+
/**
|
|
78
|
+
* Adds both inside and slider dataZoom.
|
|
79
|
+
*/
|
|
80
|
+
setDataZoom(zoomOptions: DataZoomComponentOption): this;
|
|
81
|
+
setGrid(gridOption: GridOption): this;
|
|
82
|
+
/**
|
|
83
|
+
* Sets chart title and optional subtitle, centered.
|
|
84
|
+
*/
|
|
85
|
+
setTitle(text: string, subtext?: string): this;
|
|
86
|
+
/**
|
|
87
|
+
* Applies a partial style to all existing series (e.g., { smooth: true, symbol: 'none' }).
|
|
88
|
+
*/
|
|
89
|
+
setSeriesStyle(style: Partial<LineSeriesOption>): this;
|
|
90
|
+
/**
|
|
91
|
+
* Adds a marker event to the chart.
|
|
92
|
+
*/
|
|
93
|
+
addMarkerEvents(data: MarkerEvent[], widthLine?: number): this;
|
|
94
|
+
/**
|
|
95
|
+
* Adds a marker area event to the chart.
|
|
96
|
+
*/
|
|
97
|
+
addMarkArea(data: MarkArea[]): this;
|
|
98
|
+
private getIcon;
|
|
99
|
+
addMarkerPoint(id: number, data: {
|
|
100
|
+
dimName: string;
|
|
101
|
+
timestamp: number;
|
|
102
|
+
name?: string;
|
|
103
|
+
}, options?: Partial<MarkerPointOption>): this;
|
|
104
|
+
private isNumberArray;
|
|
105
|
+
/**
|
|
106
|
+
* Creates the series data
|
|
107
|
+
*/
|
|
108
|
+
private createSeriesData;
|
|
109
|
+
/**
|
|
110
|
+
* Search for the dimension key and timestamp
|
|
111
|
+
*/
|
|
112
|
+
private searchValueByDimensionKeyAndTimestamp;
|
|
113
|
+
/**
|
|
114
|
+
* Return the percentage fields in the dataset
|
|
115
|
+
*/
|
|
116
|
+
private detectPercentageFields;
|
|
117
|
+
build(): this;
|
|
118
|
+
getDimensionKeys(): {
|
|
119
|
+
y: string[];
|
|
120
|
+
x: string;
|
|
121
|
+
};
|
|
122
|
+
getLegendStatus(): Record<string, boolean>;
|
|
123
|
+
getTotalRows(): number;
|
|
124
|
+
private isSimpleObject;
|
|
125
|
+
private isRecordArray;
|
|
126
|
+
private isNumberMatrix;
|
|
127
|
+
getRangeValues(): any[];
|
|
128
|
+
toggleMarkers(id: number, dimName: string, shape: string): this | undefined;
|
|
129
|
+
}
|
|
130
|
+
export {};
|
|
131
|
+
//# sourceMappingURL=TimeSeriesChartBuilder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TimeSeriesChartBuilder.d.ts","sourceRoot":"","sources":["../src/lib/TimeSeriesChartBuilder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,699 @@
|
|
|
1
|
+
import {} from 'echarts';
|
|
2
|
+
import {} from './';
|
|
3
|
+
export class TimeSeriesChartBuilder {
|
|
4
|
+
ECharts;
|
|
5
|
+
builderConfig = {
|
|
6
|
+
externalManagerLegend: false
|
|
7
|
+
};
|
|
8
|
+
option = {};
|
|
9
|
+
yDimensions;
|
|
10
|
+
yDimensionNames;
|
|
11
|
+
_tsColumn = '_ts';
|
|
12
|
+
constructor(instance, builderConfig) {
|
|
13
|
+
this.ECharts = instance;
|
|
14
|
+
this.builderConfig = { ...this.builderConfig, ...builderConfig };
|
|
15
|
+
this.option.animation = false;
|
|
16
|
+
this.option.legend = this.builderConfig.externalManagerLegend
|
|
17
|
+
? {
|
|
18
|
+
show: false,
|
|
19
|
+
selected: {}
|
|
20
|
+
}
|
|
21
|
+
: {
|
|
22
|
+
top: '5%',
|
|
23
|
+
selected: {}
|
|
24
|
+
};
|
|
25
|
+
this.option.grid = {
|
|
26
|
+
top: '10%',
|
|
27
|
+
left: '3%',
|
|
28
|
+
right: '4%',
|
|
29
|
+
bottom: '15%',
|
|
30
|
+
containLabel: true
|
|
31
|
+
};
|
|
32
|
+
this.option.dataZoom = [
|
|
33
|
+
{
|
|
34
|
+
type: 'inside',
|
|
35
|
+
filterMode: 'filter',
|
|
36
|
+
zoomOnMouseWheel: true,
|
|
37
|
+
moveOnMouseMove: true,
|
|
38
|
+
realtime: true,
|
|
39
|
+
start: 45,
|
|
40
|
+
end: 55
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
top: '86%',
|
|
44
|
+
left: '8%',
|
|
45
|
+
right: '8%',
|
|
46
|
+
bottom: '5%',
|
|
47
|
+
type: 'slider',
|
|
48
|
+
show: true,
|
|
49
|
+
filterMode: 'filter',
|
|
50
|
+
realtime: false
|
|
51
|
+
}
|
|
52
|
+
];
|
|
53
|
+
this.option.tooltip = {
|
|
54
|
+
trigger: 'axis',
|
|
55
|
+
axisPointer: { type: 'cross' }
|
|
56
|
+
};
|
|
57
|
+
this.option.xAxis = {
|
|
58
|
+
type: 'time',
|
|
59
|
+
axisLine: { show: true }
|
|
60
|
+
};
|
|
61
|
+
this.option.yAxis = [
|
|
62
|
+
{
|
|
63
|
+
type: 'value',
|
|
64
|
+
scale: true,
|
|
65
|
+
splitLine: { show: false },
|
|
66
|
+
axisLine: { show: true, lineStyle: { type: 'dashed' } }
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
type: 'value',
|
|
70
|
+
scale: true,
|
|
71
|
+
splitLine: { show: false },
|
|
72
|
+
axisLine: { show: true, lineStyle: { type: 'dashed' } },
|
|
73
|
+
axisLabel: {
|
|
74
|
+
formatter: (value) => `${value.toFixed(2)}%`
|
|
75
|
+
},
|
|
76
|
+
name: '%'
|
|
77
|
+
}
|
|
78
|
+
];
|
|
79
|
+
this.option.dataset = {
|
|
80
|
+
dimensions: [],
|
|
81
|
+
source: []
|
|
82
|
+
};
|
|
83
|
+
this.option.series = [];
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Accepts data as rows: [timestamp, v1, v2, ...]
|
|
87
|
+
* Automatically generates line series for each value column (>=1).
|
|
88
|
+
*/
|
|
89
|
+
setDataset(data, yDimensionsNames) {
|
|
90
|
+
if (!Array.isArray(data)) {
|
|
91
|
+
this.setDataByObjectSimple(data, yDimensionsNames);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
if (data.length < 2) {
|
|
95
|
+
throw new Error('Minimum data length is 2.');
|
|
96
|
+
}
|
|
97
|
+
if (this.isNumberArray(data)) {
|
|
98
|
+
if (!yDimensionsNames?.length) {
|
|
99
|
+
throw new Error('Requires yDimensionsNames. e.g. ["v1", "v2", "v3"]');
|
|
100
|
+
}
|
|
101
|
+
this.setDatasetByArray(data, yDimensionsNames);
|
|
102
|
+
}
|
|
103
|
+
else if (this.isRecordArray(data)) {
|
|
104
|
+
this.setDataByObject(data, yDimensionsNames);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
throw new Error('Data must be an array');
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return this.build();
|
|
111
|
+
}
|
|
112
|
+
toggleLegend(column) {
|
|
113
|
+
if (!column || !this.ECharts)
|
|
114
|
+
return this;
|
|
115
|
+
const selected = this.getColumnsSelected();
|
|
116
|
+
selected[column] = !selected[column];
|
|
117
|
+
this.ECharts.dispatchAction({
|
|
118
|
+
type: 'legendToggleSelect',
|
|
119
|
+
name: column
|
|
120
|
+
});
|
|
121
|
+
return this;
|
|
122
|
+
}
|
|
123
|
+
goToZoom(start, end) {
|
|
124
|
+
this.ECharts.dispatchAction({
|
|
125
|
+
type: 'dataZoom',
|
|
126
|
+
dataZoomIndex: 0,
|
|
127
|
+
start,
|
|
128
|
+
end
|
|
129
|
+
});
|
|
130
|
+
return this;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* data: [1658870400, 823, 95.8, ...]
|
|
134
|
+
* dimensionsNames: ['_ts', 'price', 'otherColumn', ...]
|
|
135
|
+
*/
|
|
136
|
+
setDatasetByArray(data, dimensionsNames, xAxisName) {
|
|
137
|
+
// Build series based on number of columns (minus the time column).
|
|
138
|
+
const columns = Array.isArray(data) && data.length > 0 ? data[0].length : 0;
|
|
139
|
+
const totalCol = Math.max(0, columns);
|
|
140
|
+
if (totalCol !== dimensionsNames?.length) {
|
|
141
|
+
throw new Error(`Dimensions length ${dimensionsNames?.length} does not match total columns ${totalCol}.`);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* First column is the time dimension.
|
|
145
|
+
* ------
|
|
146
|
+
* _ts |
|
|
147
|
+
* ------
|
|
148
|
+
*/
|
|
149
|
+
const timeDimensionKey = dimensionsNames.shift();
|
|
150
|
+
if (timeDimensionKey === undefined) {
|
|
151
|
+
throw new Error('No time dimension found.');
|
|
152
|
+
}
|
|
153
|
+
this._tsColumn = timeDimensionKey;
|
|
154
|
+
/**
|
|
155
|
+
* TimeDimensionName is the name of the time dimension.
|
|
156
|
+
*/
|
|
157
|
+
const timeDimensionName = xAxisName || this._tsColumn;
|
|
158
|
+
/**
|
|
159
|
+
* YDimensions are the column names.
|
|
160
|
+
* --------------------------------------------------------
|
|
161
|
+
* Column 1 | Column 2 | Column 3 | Column 4 | Column 5
|
|
162
|
+
* --------------------------------------------------------
|
|
163
|
+
*/
|
|
164
|
+
this.yDimensions = dimensionsNames;
|
|
165
|
+
this.yDimensionNames = dimensionsNames;
|
|
166
|
+
/**
|
|
167
|
+
* Dataset is an array of rows.
|
|
168
|
+
* --------------------------------------------------------------------------------
|
|
169
|
+
* Dimensions | TIME | Column 1 | Column 2 | Column 3 | Column 4 | Column 5 |
|
|
170
|
+
* --------------------------------------------------------------------------------
|
|
171
|
+
* Source | 1658870400 | 32.4 | 32.7 | 32.8 | 32.9 | 32.5 |
|
|
172
|
+
* --------------------------------------------------------------------------------
|
|
173
|
+
*/
|
|
174
|
+
if (this.option.dataset && !Array.isArray(this.option.dataset)) {
|
|
175
|
+
this.option.dataset.dimensions = [timeDimensionKey, ...this.yDimensions];
|
|
176
|
+
this.option.dataset.source = data;
|
|
177
|
+
}
|
|
178
|
+
this.createSeriesData(this._tsColumn, timeDimensionName);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Data is an array of objects.
|
|
182
|
+
* [
|
|
183
|
+
* {_ts: 1658870400, price: 823, otherColumn: 95.8},
|
|
184
|
+
* {...}
|
|
185
|
+
* ]
|
|
186
|
+
*/
|
|
187
|
+
setDataByObject(data, dimensionsNames) {
|
|
188
|
+
if (data.length < 2) {
|
|
189
|
+
throw new Error('Minimum data length is 2.');
|
|
190
|
+
}
|
|
191
|
+
// All dimensions are obtained based on the keys of the first element in the array.
|
|
192
|
+
// The first dimension, corresponding to time, is separated.
|
|
193
|
+
const dimensionKeys = Object.keys(data[0]);
|
|
194
|
+
const timeDimensionKey = dimensionKeys.shift();
|
|
195
|
+
if (timeDimensionKey === undefined) {
|
|
196
|
+
throw new Error('No time dimension found.');
|
|
197
|
+
}
|
|
198
|
+
this._tsColumn = timeDimensionKey;
|
|
199
|
+
// If custom dimension names are specified, those values will be used.
|
|
200
|
+
// By default, the dimensions will keep the same names as the original keys.
|
|
201
|
+
const timeDimensionName = dimensionsNames ? dimensionsNames.shift() : this._tsColumn;
|
|
202
|
+
/**
|
|
203
|
+
* `yDimensions` represents all data keys except the time dimension.
|
|
204
|
+
* -------------------
|
|
205
|
+
* price | otherColumn
|
|
206
|
+
* -------------------
|
|
207
|
+
*/
|
|
208
|
+
this.yDimensions = dimensionKeys;
|
|
209
|
+
// If custom dimension names are specified, those values will be used.
|
|
210
|
+
// By default, the dimensions will keep the same names as the original keys.
|
|
211
|
+
this.yDimensionNames = dimensionsNames || dimensionKeys;
|
|
212
|
+
if (this.option.dataset && !Array.isArray(this.option.dataset)) {
|
|
213
|
+
this.option.dataset.dimensions = [this._tsColumn, ...this.yDimensions];
|
|
214
|
+
this.option.dataset.source = data;
|
|
215
|
+
}
|
|
216
|
+
this.createSeriesData(this._tsColumn, timeDimensionName);
|
|
217
|
+
}
|
|
218
|
+
setDataByObjectSimple(data, dimensionsNames) {
|
|
219
|
+
// All dimensions are obtained based on the keys of the first element in the array.
|
|
220
|
+
// The first dimension, corresponding to time, is separated.
|
|
221
|
+
const dimensionKeys = Object.keys(data);
|
|
222
|
+
const timeDimensionKey = dimensionKeys.shift();
|
|
223
|
+
if (timeDimensionKey === undefined) {
|
|
224
|
+
throw new Error('No time dimension found.');
|
|
225
|
+
}
|
|
226
|
+
this._tsColumn = timeDimensionKey;
|
|
227
|
+
// If custom dimension names are specified, those values will be used.
|
|
228
|
+
// By default, the dimensions will keep the same names as the original keys.
|
|
229
|
+
const timeDimensionName = dimensionsNames ? dimensionsNames.shift() : this._tsColumn;
|
|
230
|
+
/**
|
|
231
|
+
* `yDimensions` represents all data keys except the time dimension.
|
|
232
|
+
* -------------------
|
|
233
|
+
* price | otherColumn
|
|
234
|
+
* -------------------
|
|
235
|
+
*/
|
|
236
|
+
this.yDimensions = dimensionKeys;
|
|
237
|
+
// If custom dimension names are specified, those values will be used.
|
|
238
|
+
// By default, the dimensions will keep the same names as the original keys.
|
|
239
|
+
this.yDimensionNames = dimensionsNames || dimensionKeys;
|
|
240
|
+
if (this.option.dataset && !Array.isArray(this.option.dataset)) {
|
|
241
|
+
this.option.dataset.dimensions = [this._tsColumn, ...this.yDimensions];
|
|
242
|
+
this.option.dataset.source = data;
|
|
243
|
+
}
|
|
244
|
+
this.createSeriesData(this._tsColumn, timeDimensionName);
|
|
245
|
+
}
|
|
246
|
+
addDimension(data, dimName) {
|
|
247
|
+
this.yDimensions.push(dimName);
|
|
248
|
+
this.yDimensionNames?.push(dimName);
|
|
249
|
+
if (this.option.dataset && !Array.isArray(this.option.dataset) && this.option.dataset.source) {
|
|
250
|
+
this.option.dataset.dimensions?.push(dimName);
|
|
251
|
+
Object.assign(this.option.dataset.source, data);
|
|
252
|
+
}
|
|
253
|
+
Object.keys(data).forEach((key) => this.addSeries(key, dimName, true));
|
|
254
|
+
this.build();
|
|
255
|
+
return this;
|
|
256
|
+
}
|
|
257
|
+
getColumnsSelected() {
|
|
258
|
+
const selected = this.option.legend.selected;
|
|
259
|
+
return selected;
|
|
260
|
+
}
|
|
261
|
+
addSeries(dim, dimName, isSelected) {
|
|
262
|
+
const percentageFields = this.detectPercentageFields();
|
|
263
|
+
const isPercentage = percentageFields.includes(dim);
|
|
264
|
+
const selected = this.getColumnsSelected();
|
|
265
|
+
Object.assign(selected, { [dimName]: isSelected });
|
|
266
|
+
const series = this.option.series;
|
|
267
|
+
series.push({
|
|
268
|
+
type: 'line',
|
|
269
|
+
animation: false,
|
|
270
|
+
id: dim,
|
|
271
|
+
name: dimName,
|
|
272
|
+
encode: { x: this._tsColumn, y: dim },
|
|
273
|
+
emphasis: {
|
|
274
|
+
focus: 'none',
|
|
275
|
+
disabled: true
|
|
276
|
+
},
|
|
277
|
+
connectNulls: false,
|
|
278
|
+
smooth: false,
|
|
279
|
+
sampling: 'lttb',
|
|
280
|
+
showSymbol: false,
|
|
281
|
+
progressive: 4000,
|
|
282
|
+
progressiveThreshold: 3000,
|
|
283
|
+
progressiveChunkMode: 'mod',
|
|
284
|
+
silent: true,
|
|
285
|
+
clip: true,
|
|
286
|
+
lineStyle: { width: 1 },
|
|
287
|
+
yAxisIndex: isPercentage ? 1 : 0,
|
|
288
|
+
label: {
|
|
289
|
+
show: true,
|
|
290
|
+
backgroundColor: '#000000ff',
|
|
291
|
+
color: '#fff',
|
|
292
|
+
fontSize: 10,
|
|
293
|
+
fontWeight: 'bold',
|
|
294
|
+
borderRadius: 3,
|
|
295
|
+
padding: [5, 5, 5, 5],
|
|
296
|
+
position: 'inside',
|
|
297
|
+
formatter(params) {
|
|
298
|
+
if (!params.seriesId || !params.data)
|
|
299
|
+
return '';
|
|
300
|
+
const value = params.data;
|
|
301
|
+
if (value[params.seriesId]) {
|
|
302
|
+
return `${value[params.seriesId].toFixed(2)}${isPercentage ? '%' : ''}`;
|
|
303
|
+
}
|
|
304
|
+
const idx = params.componentIndex + 1;
|
|
305
|
+
if (value[idx]) {
|
|
306
|
+
return `${value[idx].toFixed(2)}${isPercentage ? '%' : ''}`;
|
|
307
|
+
}
|
|
308
|
+
return '-';
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Tooltip bound to axis with a crosshair pointer.
|
|
315
|
+
*/
|
|
316
|
+
setAxisTooltip() {
|
|
317
|
+
this.option.tooltip = {
|
|
318
|
+
...this.option.tooltip
|
|
319
|
+
};
|
|
320
|
+
return this;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Legend with a custom icon (e.g., 'circle', 'rect').
|
|
324
|
+
*/
|
|
325
|
+
setLegendIcon(icon) {
|
|
326
|
+
this.option.legend = {
|
|
327
|
+
...this.option.legend,
|
|
328
|
+
icon
|
|
329
|
+
};
|
|
330
|
+
return this;
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Adds both inside and slider dataZoom.
|
|
334
|
+
*/
|
|
335
|
+
setDataZoom(zoomOptions) {
|
|
336
|
+
this.option.dataZoom = zoomOptions;
|
|
337
|
+
return this;
|
|
338
|
+
}
|
|
339
|
+
setGrid(gridOption) {
|
|
340
|
+
this.option.grid = {
|
|
341
|
+
...this.option.grid,
|
|
342
|
+
...gridOption
|
|
343
|
+
};
|
|
344
|
+
return this;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Sets chart title and optional subtitle, centered.
|
|
348
|
+
*/
|
|
349
|
+
setTitle(text, subtext) {
|
|
350
|
+
this.option.title = {
|
|
351
|
+
...this.option.title,
|
|
352
|
+
text,
|
|
353
|
+
subtext
|
|
354
|
+
};
|
|
355
|
+
return this;
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Applies a partial style to all existing series (e.g., { smooth: true, symbol: 'none' }).
|
|
359
|
+
*/
|
|
360
|
+
setSeriesStyle(style) {
|
|
361
|
+
if (Array.isArray(this.option.series)) {
|
|
362
|
+
this.option.series = this.option.series.map((s) => ({
|
|
363
|
+
...s,
|
|
364
|
+
...style
|
|
365
|
+
}));
|
|
366
|
+
}
|
|
367
|
+
return this;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Adds a marker event to the chart.
|
|
371
|
+
*/
|
|
372
|
+
addMarkerEvents(data, widthLine = 1) {
|
|
373
|
+
if (!Array.isArray(this.option.series)) {
|
|
374
|
+
throw new Error('Series must be an array');
|
|
375
|
+
}
|
|
376
|
+
for (const event of data) {
|
|
377
|
+
const position = event.position || 'aboveBar';
|
|
378
|
+
this.option.series.push({
|
|
379
|
+
type: 'line',
|
|
380
|
+
data: [],
|
|
381
|
+
markLine: {
|
|
382
|
+
symbol: this.getIcon(event.icon || 'none'),
|
|
383
|
+
symbolSize: [15, 15],
|
|
384
|
+
symbolOffset: [
|
|
385
|
+
[0, 15],
|
|
386
|
+
[0, 15]
|
|
387
|
+
],
|
|
388
|
+
label: {
|
|
389
|
+
position: position === 'aboveBar' ? 'insideEnd' : 'insideStart',
|
|
390
|
+
offset: position === 'aboveBar' ? [-35, 0] : [35, 0],
|
|
391
|
+
distance: 0,
|
|
392
|
+
color: 'white',
|
|
393
|
+
formatter: event.name || '',
|
|
394
|
+
fontSize: 12,
|
|
395
|
+
fontFamily: 'Arial',
|
|
396
|
+
fontStyle: 'normal',
|
|
397
|
+
padding: 8,
|
|
398
|
+
backgroundColor: event.name ? (event.color?.toString() ?? 'white') : undefined,
|
|
399
|
+
borderRadius: 4
|
|
400
|
+
},
|
|
401
|
+
emphasis: {
|
|
402
|
+
disabled: true
|
|
403
|
+
},
|
|
404
|
+
lineStyle: {
|
|
405
|
+
color: event.color,
|
|
406
|
+
width: widthLine,
|
|
407
|
+
type: 'dashed'
|
|
408
|
+
},
|
|
409
|
+
data: event.xAxis.map((x) => ({ xAxis: x }))
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
return this.build();
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Adds a marker area event to the chart.
|
|
417
|
+
*/
|
|
418
|
+
addMarkArea(data) {
|
|
419
|
+
if (!Array.isArray(this.option.series)) {
|
|
420
|
+
throw new Error('Series must be an array');
|
|
421
|
+
}
|
|
422
|
+
for (const event of data) {
|
|
423
|
+
this.option.series.push({
|
|
424
|
+
type: 'line',
|
|
425
|
+
data: [],
|
|
426
|
+
markArea: {
|
|
427
|
+
itemStyle: {
|
|
428
|
+
color: event.color || 'rgba(0, 17, 255, 0.1)'
|
|
429
|
+
},
|
|
430
|
+
label: {
|
|
431
|
+
position: 'top',
|
|
432
|
+
formatter: event.name || '',
|
|
433
|
+
fontWeight: 'bold',
|
|
434
|
+
fontSize: 11
|
|
435
|
+
},
|
|
436
|
+
data: [
|
|
437
|
+
[
|
|
438
|
+
{
|
|
439
|
+
xAxis: event.xAxis[0]
|
|
440
|
+
},
|
|
441
|
+
{
|
|
442
|
+
xAxis: event.xAxis[1]
|
|
443
|
+
}
|
|
444
|
+
]
|
|
445
|
+
]
|
|
446
|
+
}
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
this.addMarkerEvents(data.map((e) => ({ ...e, name: undefined })), 1);
|
|
450
|
+
return this.build();
|
|
451
|
+
}
|
|
452
|
+
getIcon(icon) {
|
|
453
|
+
const arrowUpPath = 'path://M7.414 27.414l16.586-16.586v7.172c0 1.105 0.895 2 2 2s2-0.895 2-2v-12c0-0.809-0.487-1.538-1.235-1.848-0.248-0.103-0.508-0.151-0.765-0.151v-0.001h-12c-1.105 0-2 0.895-2 2s0.895 2 2 2h7.172l-16.586 16.586c-0.391 0.39-0.586 0.902-0.586 1.414s0.195 1.024 0.586 1.414c0.781 0.781 2.047 0.781 2.828 0z';
|
|
454
|
+
const arrowDownPath = 'path://M4.586 7.414l16.586 16.586h-7.171c-1.105 0-2 0.895-2 2s0.895 2 2 2h12c0.809 0 1.538-0.487 1.848-1.235 0.103-0.248 0.151-0.508 0.151-0.765h0.001v-12c0-1.105-0.895-2-2-2s-2 0.895-2 2v7.172l-16.586-16.586c-0.391-0.391-0.902-0.586-1.414-0.586s-1.024 0.195-1.414 0.586c-0.781 0.781-0.781 2.047 0 2.828z';
|
|
455
|
+
const circlePath = 'path://M16 0c-8.837 0-16 7.163-16 16s7.163 16 16 16 16-7.163 16-16-7.163-16-16-16zM16 28c-6.627 0-12-5.373-12-12s5.373-12 12-12c6.627 0 12 5.373 12 12s-5.373 12-12 12z';
|
|
456
|
+
if (icon === 'arrowDown') {
|
|
457
|
+
return arrowDownPath;
|
|
458
|
+
}
|
|
459
|
+
if (icon === 'arrowUp') {
|
|
460
|
+
return arrowUpPath;
|
|
461
|
+
}
|
|
462
|
+
if (icon === 'circle') {
|
|
463
|
+
return circlePath;
|
|
464
|
+
}
|
|
465
|
+
return icon;
|
|
466
|
+
}
|
|
467
|
+
addMarkerPoint(id, data, options) {
|
|
468
|
+
try {
|
|
469
|
+
const opt = {
|
|
470
|
+
icon: 'none',
|
|
471
|
+
position: 'inside',
|
|
472
|
+
symbolSize: 18,
|
|
473
|
+
color: 'black',
|
|
474
|
+
...options
|
|
475
|
+
};
|
|
476
|
+
if (!Array.isArray(this.option.series)) {
|
|
477
|
+
throw new Error('Series must be an array');
|
|
478
|
+
}
|
|
479
|
+
if (Array.isArray(this.option.dataset)) {
|
|
480
|
+
throw new Error('Series must be an array');
|
|
481
|
+
}
|
|
482
|
+
// Search for the dimension
|
|
483
|
+
const seriesDimension = this.option.series
|
|
484
|
+
.filter((s) => s.encode && s.encode.y)
|
|
485
|
+
.find((s) => {
|
|
486
|
+
return s.encode.y === data.dimName;
|
|
487
|
+
});
|
|
488
|
+
if (!seriesDimension)
|
|
489
|
+
throw new Error(`Dimension ${data.dimName} not found`);
|
|
490
|
+
let value = this.searchValueByDimensionKeyAndTimestamp(data.dimName, data.timestamp);
|
|
491
|
+
/**
|
|
492
|
+
* Creates a data point for the marker
|
|
493
|
+
*/
|
|
494
|
+
const dataPoint = () => {
|
|
495
|
+
return {
|
|
496
|
+
name: `markerpoint-${id}`,
|
|
497
|
+
coord: [data.timestamp, value],
|
|
498
|
+
symbol: this.getIcon(opt.icon),
|
|
499
|
+
symbolSize: opt.symbolSize,
|
|
500
|
+
symbolOffset: [0, -1 * (opt.symbolSize * 3)],
|
|
501
|
+
itemStyle: {
|
|
502
|
+
color: opt.color,
|
|
503
|
+
borderColor: opt.color,
|
|
504
|
+
borderWidth: 2
|
|
505
|
+
},
|
|
506
|
+
label: {
|
|
507
|
+
show: true,
|
|
508
|
+
offset: [0, 30],
|
|
509
|
+
formatter: data.name && Number(data.name)
|
|
510
|
+
? Number(data.name).toFixed(2)
|
|
511
|
+
: (data.name ?? value.toFixed(2)),
|
|
512
|
+
fontSize: 12,
|
|
513
|
+
fontWeight: 'bold',
|
|
514
|
+
color: 'white',
|
|
515
|
+
backgroundColor: opt.color,
|
|
516
|
+
padding: 4,
|
|
517
|
+
borderRadius: 4
|
|
518
|
+
},
|
|
519
|
+
z: 11
|
|
520
|
+
};
|
|
521
|
+
};
|
|
522
|
+
// Create markPoint if it doesn't exist
|
|
523
|
+
if (!seriesDimension.markPoint) {
|
|
524
|
+
seriesDimension.markPoint = {
|
|
525
|
+
data: [dataPoint()]
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
else {
|
|
529
|
+
seriesDimension.markPoint.data.push(dataPoint());
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
catch (error) {
|
|
533
|
+
console.error(error.message);
|
|
534
|
+
}
|
|
535
|
+
return this;
|
|
536
|
+
}
|
|
537
|
+
isNumberArray(arr) {
|
|
538
|
+
return Array.isArray(arr[0]);
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Creates the series data
|
|
542
|
+
*/
|
|
543
|
+
createSeriesData(timeDimensionKey, timeDimensionName) {
|
|
544
|
+
if (!this.yDimensions?.length || !this.yDimensionNames?.length) {
|
|
545
|
+
throw new Error('No dimensions found.');
|
|
546
|
+
}
|
|
547
|
+
if (this.yDimensions.length !== this.yDimensionNames.length) {
|
|
548
|
+
throw new Error(`Dimensions length ${this.yDimensionNames.length} does not match total columns ${this.yDimensions.length}.`);
|
|
549
|
+
}
|
|
550
|
+
this.option.xAxis = { type: 'time', name: timeDimensionName };
|
|
551
|
+
this.yDimensions.map((dim, inx) => this.addSeries(dim, this.yDimensionNames[inx], (this.yDimensions.length > 1 && dim === 'price') || this.yDimensions.length === 1));
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Search for the dimension key and timestamp
|
|
555
|
+
*/
|
|
556
|
+
searchValueByDimensionKeyAndTimestamp(yDimKey, timestamp) {
|
|
557
|
+
const dataset = this.option.dataset;
|
|
558
|
+
if (!dataset.dimensions.find((d) => d === yDimKey)) {
|
|
559
|
+
throw new Error('No source data or dimensions found. Before loading data');
|
|
560
|
+
}
|
|
561
|
+
if (Array.isArray(dataset.source)) {
|
|
562
|
+
if (this.isNumberArray(dataset.source)) {
|
|
563
|
+
const dataFind = dataset.source.find((row) => {
|
|
564
|
+
return row[0] === timestamp;
|
|
565
|
+
});
|
|
566
|
+
if (!dataFind) {
|
|
567
|
+
throw new Error(`No data found in timestamp ${timestamp}`);
|
|
568
|
+
}
|
|
569
|
+
const yDimensionKey = dataset.dimensions.findIndex((d) => d === yDimKey);
|
|
570
|
+
return dataFind[yDimensionKey];
|
|
571
|
+
}
|
|
572
|
+
else if (this.isRecordArray(dataset.source)) {
|
|
573
|
+
const dataFind = dataset.source.find((row) => {
|
|
574
|
+
return row[this._tsColumn] === timestamp;
|
|
575
|
+
});
|
|
576
|
+
if (!dataFind) {
|
|
577
|
+
throw new Error(`No data found in timestamp ${timestamp}`);
|
|
578
|
+
}
|
|
579
|
+
return dataFind[yDimKey];
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
else {
|
|
583
|
+
const dataFind = dataset.source[this._tsColumn].indexOf(timestamp);
|
|
584
|
+
if (dataFind === -1) {
|
|
585
|
+
throw new Error(`No data found in timestamp ${timestamp}`);
|
|
586
|
+
}
|
|
587
|
+
return dataset.source[yDimKey][dataFind];
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Return the percentage fields in the dataset
|
|
592
|
+
*/
|
|
593
|
+
detectPercentageFields() {
|
|
594
|
+
if (!this.yDimensions?.length) {
|
|
595
|
+
throw new Error('No dimensions found.');
|
|
596
|
+
}
|
|
597
|
+
const percentFields = this.yDimensions.filter((key) => !key.startsWith('_') && key.endsWith('%'));
|
|
598
|
+
return percentFields;
|
|
599
|
+
}
|
|
600
|
+
build() {
|
|
601
|
+
const option = this.ECharts.getOption();
|
|
602
|
+
if (option &&
|
|
603
|
+
option.dataZoom &&
|
|
604
|
+
Array.isArray(option.dataZoom) &&
|
|
605
|
+
Array.isArray(this.option.dataZoom)) {
|
|
606
|
+
this.option.dataZoom[0].start = option.dataZoom[0].start;
|
|
607
|
+
this.option.dataZoom[0].end = option.dataZoom[0].end;
|
|
608
|
+
}
|
|
609
|
+
this.ECharts.setOption(this.option, {
|
|
610
|
+
lazyUpdate: true,
|
|
611
|
+
notMerge: false,
|
|
612
|
+
replaceMerge: ['dataset']
|
|
613
|
+
});
|
|
614
|
+
return this;
|
|
615
|
+
}
|
|
616
|
+
getDimensionKeys() {
|
|
617
|
+
return {
|
|
618
|
+
y: this.yDimensionNames,
|
|
619
|
+
x: this._tsColumn
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
getLegendStatus() {
|
|
623
|
+
return this.getColumnsSelected();
|
|
624
|
+
}
|
|
625
|
+
getTotalRows() {
|
|
626
|
+
const dataset = this.option.dataset;
|
|
627
|
+
if (Array.isArray(dataset.source)) {
|
|
628
|
+
return dataset.source.length;
|
|
629
|
+
}
|
|
630
|
+
else {
|
|
631
|
+
return dataset.source[this._tsColumn].length;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
isSimpleObject(s) {
|
|
635
|
+
return !Array.isArray(s) && typeof s === 'object' && s !== null;
|
|
636
|
+
}
|
|
637
|
+
isRecordArray(source) {
|
|
638
|
+
return Array.isArray(source) && (source.length === 0 || !Array.isArray(source[0]));
|
|
639
|
+
}
|
|
640
|
+
isNumberMatrix(source) {
|
|
641
|
+
return Array.isArray(source) && (source.length === 0 || Array.isArray(source[0]));
|
|
642
|
+
}
|
|
643
|
+
getRangeValues() {
|
|
644
|
+
const dataset = this.option.dataset;
|
|
645
|
+
const source = dataset.source;
|
|
646
|
+
// ---- Record<string, any>[] ----
|
|
647
|
+
if (this.isRecordArray(source)) {
|
|
648
|
+
if (!source.length)
|
|
649
|
+
return [0, 0];
|
|
650
|
+
const objSource = source;
|
|
651
|
+
const firstRow = objSource[0];
|
|
652
|
+
const lastRow = objSource[objSource.length - 1];
|
|
653
|
+
const first = firstRow[this._tsColumn];
|
|
654
|
+
const last = lastRow[this._tsColumn];
|
|
655
|
+
return [first, last];
|
|
656
|
+
}
|
|
657
|
+
// ---- number[][] ----
|
|
658
|
+
if (this.isNumberMatrix(source)) {
|
|
659
|
+
if (!source.length)
|
|
660
|
+
return [0, 0];
|
|
661
|
+
const matrixSource = source;
|
|
662
|
+
const tsIndex = dataset.dimensions.indexOf(this._tsColumn);
|
|
663
|
+
const idx = tsIndex === -1 ? 0 : tsIndex;
|
|
664
|
+
const firstRow = matrixSource[0];
|
|
665
|
+
const lastRow = matrixSource[matrixSource.length - 1];
|
|
666
|
+
const first = firstRow[idx];
|
|
667
|
+
const last = lastRow[idx];
|
|
668
|
+
return [first, last];
|
|
669
|
+
}
|
|
670
|
+
// ---- Record<string, number[]> ----
|
|
671
|
+
if (this.isSimpleObject(source)) {
|
|
672
|
+
const col = source[this._tsColumn];
|
|
673
|
+
if (!col?.length)
|
|
674
|
+
return [0, 0];
|
|
675
|
+
return [col[0], col[col.length - 1]];
|
|
676
|
+
}
|
|
677
|
+
return [0, 0];
|
|
678
|
+
}
|
|
679
|
+
toggleMarkers(id, dimName, shape) {
|
|
680
|
+
if (!Array.isArray(this.option.series)) {
|
|
681
|
+
throw new Error('Series must be an array');
|
|
682
|
+
}
|
|
683
|
+
if (Array.isArray(this.option.dataset)) {
|
|
684
|
+
throw new Error('Series must be an array');
|
|
685
|
+
}
|
|
686
|
+
// Search for the dimension
|
|
687
|
+
const seriesDimension = this.option.series.find((s) => {
|
|
688
|
+
return s.encode && s.encode.y && s.encode.y === dimName;
|
|
689
|
+
});
|
|
690
|
+
const markerPoints = seriesDimension?.markPoint.data;
|
|
691
|
+
const point = markerPoints.find((mp) => mp.name === `markerpoint-${id}`);
|
|
692
|
+
if (!point) {
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
point.symbol = point.symbol === 'none' ? this.getIcon(shape) : 'none';
|
|
696
|
+
this.build();
|
|
697
|
+
return this;
|
|
698
|
+
}
|
|
699
|
+
}
|
package/dist/index.d.ts
CHANGED
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,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,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,cAAc,SAAS,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mockDataSet.d.ts","sourceRoot":"","sources":["../src/lib/mockDataSet.ts"],"names":[],"mappings":"AAAA,wBAAgB,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,OAAO;UA8BxD,CAAC,EAAE;;EAGlB"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export function createDataSet(hours, type) {
|
|
2
|
+
// Create random number between min and max
|
|
3
|
+
const random = (min = 45, max = 50) => Math.random() * max + min;
|
|
4
|
+
const rows = [];
|
|
5
|
+
const YDimensionsName = ['_ts', 'price', 'Column 2', 'Column 3', 'Column 4', 'Column 5 %'];
|
|
6
|
+
// Create a date range
|
|
7
|
+
let startDate = new Date(2025, 9, 28, 14, 0, 0).getTime();
|
|
8
|
+
// Create random data per hour
|
|
9
|
+
for (let i = 0; i < hours; i++) {
|
|
10
|
+
// Add 1 hour
|
|
11
|
+
const ts = new Date(startDate + i * 60000).getTime();
|
|
12
|
+
if (type === 'object') {
|
|
13
|
+
rows.push({
|
|
14
|
+
_ts: ts,
|
|
15
|
+
price: random(),
|
|
16
|
+
col2: random(),
|
|
17
|
+
col3: random(),
|
|
18
|
+
col4: random(),
|
|
19
|
+
'col5%': random(0, 5)
|
|
20
|
+
});
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
rows.push([ts, random(), random(), random(), random(), random()]);
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
data: rows,
|
|
30
|
+
yDimensionsNames: YDimensionsName
|
|
31
|
+
};
|
|
32
|
+
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
+
"private": false,
|
|
2
3
|
"name": "@qtsurfer/sveltecharts",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
},
|
|
4
|
+
"version": "0.4.0",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"author": "QTSurfer (https://github.com/QTSurfer)",
|
|
7
|
+
"homepage": "https://github.com/QTSurfer/svelte-timeseries/tree/main/packages/sveltecharts",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
10
|
"url": "git+https://github.com/QTSurfer/svelte-timeseries.git",
|
|
@@ -13,8 +13,7 @@
|
|
|
13
13
|
"bugs": {
|
|
14
14
|
"url": "https://github.com/QTSurfer/svelte-timeseries/issues"
|
|
15
15
|
},
|
|
16
|
-
"
|
|
17
|
-
"homepage": "https://github.com/QTSurfer/svelte-timeseries",
|
|
16
|
+
"type": "module",
|
|
18
17
|
"exports": {
|
|
19
18
|
".": {
|
|
20
19
|
"types": "./dist/index.d.ts",
|
|
@@ -22,58 +21,59 @@
|
|
|
22
21
|
}
|
|
23
22
|
},
|
|
24
23
|
"files": [
|
|
25
|
-
"package.json",
|
|
26
|
-
"README.md",
|
|
27
|
-
"LICENSE",
|
|
28
24
|
"dist",
|
|
29
|
-
"!dist/**/*.
|
|
30
|
-
"!dist/**/*.
|
|
25
|
+
"!dist/**/*.spec.*",
|
|
26
|
+
"!dist/**/*.test.*"
|
|
31
27
|
],
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
32
29
|
"peerDependencies": {
|
|
33
|
-
"echarts": "^
|
|
34
|
-
"svelte": "^
|
|
30
|
+
"echarts": "^6.0.0",
|
|
31
|
+
"svelte": "^5.43.14"
|
|
35
32
|
},
|
|
36
33
|
"devDependencies": {
|
|
37
|
-
"@eslint/eslintrc": "^3.
|
|
38
|
-
"@eslint/js": "^9.
|
|
39
|
-
"@sveltejs/adapter-auto": "^
|
|
40
|
-
"@sveltejs/kit": "^2.
|
|
41
|
-
"@sveltejs/package": "^2.
|
|
42
|
-
"@sveltejs/vite-plugin-svelte": "^
|
|
34
|
+
"@eslint/eslintrc": "^3.3.1",
|
|
35
|
+
"@eslint/js": "^9.39.1",
|
|
36
|
+
"@sveltejs/adapter-auto": "^7.0.0",
|
|
37
|
+
"@sveltejs/kit": "^2.48.6",
|
|
38
|
+
"@sveltejs/package": "^2.5.6",
|
|
39
|
+
"@sveltejs/vite-plugin-svelte": "^6.2.1",
|
|
43
40
|
"@types/eslint": "^9.6.1",
|
|
44
|
-
"@types/node": "^
|
|
45
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
46
|
-
"@typescript-eslint/parser": "^8.
|
|
47
|
-
"
|
|
48
|
-
"eslint
|
|
49
|
-
"eslint-
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"svelte": "^4.
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
41
|
+
"@types/node": "^24.10.1",
|
|
42
|
+
"@typescript-eslint/eslint-plugin": "^8.47.0",
|
|
43
|
+
"@typescript-eslint/parser": "^8.47.0",
|
|
44
|
+
"echarts": "^6.0.0",
|
|
45
|
+
"eslint": "^9.39.1",
|
|
46
|
+
"eslint-config-prettier": "^10.1.8",
|
|
47
|
+
"eslint-plugin-svelte": "^3.13.0",
|
|
48
|
+
"globals": "^16.5.0",
|
|
49
|
+
"npm-run-all2": "^8.0.4",
|
|
50
|
+
"prettier": "^3.6.2",
|
|
51
|
+
"prettier-plugin-svelte": "^3.4.0",
|
|
52
|
+
"publint": "^0.3.15",
|
|
53
|
+
"svelte": "^5.43.14",
|
|
54
|
+
"svelte-check": "^4.3.4",
|
|
55
|
+
"typescript": "^5.9.3",
|
|
56
|
+
"vite": "^7.2.4",
|
|
57
|
+
"vitest": "^4.0.12"
|
|
59
58
|
},
|
|
60
|
-
"
|
|
61
|
-
"
|
|
59
|
+
"publishConfig": {
|
|
60
|
+
"access": "public"
|
|
62
61
|
},
|
|
63
62
|
"svelte": "./dist/index.js",
|
|
64
|
-
"types": "./dist/index.d.ts",
|
|
65
|
-
"type": "module",
|
|
66
63
|
"scripts": {
|
|
67
|
-
"
|
|
68
|
-
"build": "vite build && npm run package",
|
|
69
|
-
"preview": "vite preview",
|
|
70
|
-
"package": "svelte-kit sync && svelte-package && publint",
|
|
64
|
+
"build": "run-s vite:build package",
|
|
71
65
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
72
66
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
73
|
-
"
|
|
67
|
+
"dev": "vite dev",
|
|
68
|
+
"format": "run-p format:all format:package",
|
|
69
|
+
"format:all": "prettier -l --write .",
|
|
70
|
+
"format:package": "npx prettier-package-json --write ./package.json",
|
|
71
|
+
"knip": "pnpm dlx knip",
|
|
74
72
|
"lint": "prettier --check . && eslint .",
|
|
75
|
-
"
|
|
73
|
+
"package": "svelte-kit sync && svelte-package && publint",
|
|
74
|
+
"preview": "vite preview",
|
|
76
75
|
"publishd": "pnpm publish --dry-run --no-git-checks",
|
|
77
|
-
"
|
|
76
|
+
"test": "vitest",
|
|
77
|
+
"vite:build": "vite build"
|
|
78
78
|
}
|
|
79
79
|
}
|