@qfo/qfchart 0.6.4 → 0.6.6

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.
@@ -0,0 +1,36 @@
1
+ import { SeriesRenderer } from './renderers/SeriesRenderer';
2
+ import { LineRenderer } from './renderers/LineRenderer';
3
+ import { StepRenderer } from './renderers/StepRenderer';
4
+ import { HistogramRenderer } from './renderers/HistogramRenderer';
5
+ import { ScatterRenderer } from './renderers/ScatterRenderer';
6
+ import { OHLCBarRenderer } from './renderers/OHLCBarRenderer';
7
+ import { ShapeRenderer } from './renderers/ShapeRenderer';
8
+ import { BackgroundRenderer } from './renderers/BackgroundRenderer';
9
+ import { FillRenderer } from './renderers/FillRenderer';
10
+
11
+ export class SeriesRendererFactory {
12
+ private static renderers: Map<string, SeriesRenderer> = new Map();
13
+
14
+ static {
15
+ this.register('line', new LineRenderer());
16
+ this.register('step', new StepRenderer());
17
+ this.register('histogram', new HistogramRenderer());
18
+ this.register('columns', new HistogramRenderer());
19
+ this.register('circles', new ScatterRenderer());
20
+ this.register('cross', new ScatterRenderer());
21
+ this.register('char', new ScatterRenderer());
22
+ this.register('bar', new OHLCBarRenderer());
23
+ this.register('candle', new OHLCBarRenderer());
24
+ this.register('shape', new ShapeRenderer());
25
+ this.register('background', new BackgroundRenderer());
26
+ this.register('fill', new FillRenderer());
27
+ }
28
+
29
+ public static register(style: string, renderer: SeriesRenderer) {
30
+ this.renderers.set(style, renderer);
31
+ }
32
+
33
+ public static get(style: string): SeriesRenderer {
34
+ return this.renderers.get(style) || this.renderers.get('line')!; // Default to line
35
+ }
36
+ }
@@ -0,0 +1,47 @@
1
+ import { SeriesRenderer, RenderContext } from './SeriesRenderer';
2
+
3
+ export class BackgroundRenderer implements SeriesRenderer {
4
+ render(context: RenderContext): any {
5
+ const { seriesName, xAxisIndex, yAxisIndex, dataArray, colorArray } = context;
6
+
7
+ return {
8
+ name: seriesName,
9
+ type: 'custom',
10
+ xAxisIndex: xAxisIndex,
11
+ yAxisIndex: yAxisIndex,
12
+ z: -10,
13
+ renderItem: (params: any, api: any) => {
14
+ const xVal = api.value(0);
15
+ if (isNaN(xVal)) return;
16
+
17
+ const start = api.coord([xVal, 0.5]); // Use 0.5 as a fixed Y-value within [0,1] range
18
+ const size = api.size([1, 0]);
19
+ const width = size[0];
20
+ const sys = params.coordSys;
21
+ const x = start[0] - width / 2;
22
+ const barColor = colorArray[params.dataIndex];
23
+ const val = api.value(1);
24
+
25
+ if (!barColor || val === null || val === undefined || isNaN(val)) return;
26
+
27
+ return {
28
+ type: 'rect',
29
+ shape: {
30
+ x: x,
31
+ y: sys.y,
32
+ width: width,
33
+ height: sys.height,
34
+ },
35
+ style: {
36
+ fill: barColor,
37
+ opacity: 0.3,
38
+ },
39
+ silent: true,
40
+ };
41
+ },
42
+ // Normalize data values to 0.5 (middle of [0,1] range) to prevent Y-axis scaling issues
43
+ // The actual value is only used to check if the background should render (non-null/non-NaN)
44
+ data: dataArray.map((val, i) => [i, val !== null && val !== undefined && !isNaN(val) ? 0.5 : null]),
45
+ };
46
+ }
47
+ }
@@ -0,0 +1,99 @@
1
+ import { SeriesRenderer, RenderContext } from './SeriesRenderer';
2
+ import { ColorUtils } from '../../utils/ColorUtils';
3
+
4
+ export class FillRenderer implements SeriesRenderer {
5
+ render(context: RenderContext): any {
6
+ const { seriesName, xAxisIndex, yAxisIndex, plotOptions, plotDataArrays, indicatorId, plotName } = context;
7
+ const totalDataLength = context.dataArray.length; // Use length from dataArray placeholder
8
+
9
+ // Fill plots reference other plots to fill the area between them
10
+ const plot1Key = plotOptions.plot1 ? `${indicatorId}::${plotOptions.plot1}` : null;
11
+ const plot2Key = plotOptions.plot2 ? `${indicatorId}::${plotOptions.plot2}` : null;
12
+
13
+ if (!plot1Key || !plot2Key) {
14
+ console.warn(`Fill plot "${plotName}" missing plot1 or plot2 reference`);
15
+ return null;
16
+ }
17
+
18
+ const plot1Data = plotDataArrays?.get(plot1Key);
19
+ const plot2Data = plotDataArrays?.get(plot2Key);
20
+
21
+ if (!plot1Data || !plot2Data) {
22
+ console.warn(`Fill plot "${plotName}" references non-existent plots: ${plotOptions.plot1}, ${plotOptions.plot2}`);
23
+ return null;
24
+ }
25
+
26
+ // Parse color to extract opacity
27
+ const { color: fillColor, opacity: fillOpacity } = ColorUtils.parseColor(plotOptions.color || 'rgba(128, 128, 128, 0.2)');
28
+
29
+ // Create fill data with previous values for smooth polygon rendering
30
+ const fillDataWithPrev: any[] = [];
31
+ for (let i = 0; i < totalDataLength; i++) {
32
+ const y1 = plot1Data[i];
33
+ const y2 = plot2Data[i];
34
+ const prevY1 = i > 0 ? plot1Data[i - 1] : null;
35
+ const prevY2 = i > 0 ? plot2Data[i - 1] : null;
36
+
37
+ fillDataWithPrev.push([i, y1, y2, prevY1, prevY2]);
38
+ }
39
+
40
+ // Add fill series with smooth area rendering
41
+ return {
42
+ name: seriesName,
43
+ type: 'custom',
44
+ xAxisIndex: xAxisIndex,
45
+ yAxisIndex: yAxisIndex,
46
+ z: -5, // Render behind lines but above background
47
+ renderItem: (params: any, api: any) => {
48
+ const index = params.dataIndex;
49
+
50
+ // Skip first point (no previous to connect to)
51
+ if (index === 0) return null;
52
+
53
+ const y1 = api.value(1); // Current upper
54
+ const y2 = api.value(2); // Current lower
55
+ const prevY1 = api.value(3); // Previous upper
56
+ const prevY2 = api.value(4); // Previous lower
57
+
58
+ // Skip if any value is null/NaN
59
+ if (
60
+ y1 === null ||
61
+ y2 === null ||
62
+ prevY1 === null ||
63
+ prevY2 === null ||
64
+ isNaN(y1) ||
65
+ isNaN(y2) ||
66
+ isNaN(prevY1) ||
67
+ isNaN(prevY2)
68
+ ) {
69
+ return null;
70
+ }
71
+
72
+ // Get pixel coordinates for all 4 points
73
+ const p1Prev = api.coord([index - 1, prevY1]); // Previous upper
74
+ const p1Curr = api.coord([index, y1]); // Current upper
75
+ const p2Curr = api.coord([index, y2]); // Current lower
76
+ const p2Prev = api.coord([index - 1, prevY2]); // Previous lower
77
+
78
+ // Create a smooth polygon connecting the segments
79
+ return {
80
+ type: 'polygon',
81
+ shape: {
82
+ points: [
83
+ p1Prev, // Top-left
84
+ p1Curr, // Top-right
85
+ p2Curr, // Bottom-right
86
+ p2Prev, // Bottom-left
87
+ ],
88
+ },
89
+ style: {
90
+ fill: fillColor,
91
+ opacity: fillOpacity,
92
+ },
93
+ silent: true,
94
+ };
95
+ },
96
+ data: fillDataWithPrev,
97
+ };
98
+ }
99
+ }
@@ -0,0 +1,20 @@
1
+ import { SeriesRenderer, RenderContext } from './SeriesRenderer';
2
+
3
+ export class HistogramRenderer implements SeriesRenderer {
4
+ render(context: RenderContext): any {
5
+ const { seriesName, xAxisIndex, yAxisIndex, dataArray, colorArray, plotOptions } = context;
6
+ const defaultColor = '#2962ff';
7
+
8
+ return {
9
+ name: seriesName,
10
+ type: 'bar',
11
+ xAxisIndex: xAxisIndex,
12
+ yAxisIndex: yAxisIndex,
13
+ data: dataArray.map((val, i) => ({
14
+ value: val,
15
+ itemStyle: colorArray[i] ? { color: colorArray[i] } : undefined,
16
+ })),
17
+ itemStyle: { color: plotOptions.color || defaultColor },
18
+ };
19
+ }
20
+ }
@@ -0,0 +1,44 @@
1
+ import { SeriesRenderer, RenderContext } from './SeriesRenderer';
2
+
3
+ export class LineRenderer implements SeriesRenderer {
4
+ render(context: RenderContext): any {
5
+ const { seriesName, xAxisIndex, yAxisIndex, dataArray, colorArray, plotOptions } = context;
6
+ const defaultColor = '#2962ff';
7
+
8
+ return {
9
+ name: seriesName,
10
+ type: 'custom',
11
+ xAxisIndex: xAxisIndex,
12
+ yAxisIndex: yAxisIndex,
13
+ renderItem: (params: any, api: any) => {
14
+ const index = params.dataIndex;
15
+ if (index === 0) return; // Need at least two points for a line segment
16
+
17
+ const y2 = api.value(1);
18
+ const y1 = api.value(2); // We'll store prevValue in the data
19
+
20
+ if (y2 === null || isNaN(y2) || y1 === null || isNaN(y1)) return;
21
+
22
+ const p1 = api.coord([index - 1, y1]);
23
+ const p2 = api.coord([index, y2]);
24
+
25
+ return {
26
+ type: 'line',
27
+ shape: {
28
+ x1: p1[0],
29
+ y1: p1[1],
30
+ x2: p2[0],
31
+ y2: p2[1],
32
+ },
33
+ style: {
34
+ stroke: colorArray[index] || plotOptions.color || defaultColor,
35
+ lineWidth: plotOptions.linewidth || 1,
36
+ },
37
+ silent: true,
38
+ };
39
+ },
40
+ // Data format: [index, value, prevValue]
41
+ data: dataArray.map((val, i) => [i, val, i > 0 ? dataArray[i - 1] : null]),
42
+ };
43
+ }
44
+ }
@@ -0,0 +1,161 @@
1
+ import { SeriesRenderer, RenderContext } from './SeriesRenderer';
2
+
3
+ export class OHLCBarRenderer implements SeriesRenderer {
4
+ render(context: RenderContext): any {
5
+ const { seriesName, xAxisIndex, yAxisIndex, dataArray, colorArray, optionsArray, plotOptions } = context;
6
+ const defaultColor = '#2962ff';
7
+ const isCandle = plotOptions.style === 'candle';
8
+
9
+ const ohlcData = dataArray
10
+ .map((val, i) => {
11
+ if (val === null || !Array.isArray(val) || val.length !== 4) return null;
12
+
13
+ const [open, high, low, close] = val;
14
+ const pointOpts = optionsArray[i] || {};
15
+ const color = pointOpts.color || colorArray[i] || plotOptions.color || defaultColor;
16
+ const wickColor = pointOpts.wickcolor || plotOptions.wickcolor || color;
17
+ const borderColor = pointOpts.bordercolor || plotOptions.bordercolor || wickColor;
18
+
19
+ // Store colors in value array at positions 5, 6, and 7 for access in renderItem
20
+ return [i, open, close, low, high, color, wickColor, borderColor];
21
+ })
22
+ .filter((item) => item !== null);
23
+
24
+ return {
25
+ name: seriesName,
26
+ type: 'custom',
27
+ xAxisIndex: xAxisIndex,
28
+ yAxisIndex: yAxisIndex,
29
+ renderItem: (params: any, api: any) => {
30
+ const xValue = api.value(0);
31
+ const openValue = api.value(1);
32
+ const closeValue = api.value(2);
33
+ const lowValue = api.value(3);
34
+ const highValue = api.value(4);
35
+ const color = api.value(5);
36
+ const wickColor = api.value(6);
37
+ const borderColor = api.value(7);
38
+
39
+ if (isNaN(openValue) || isNaN(closeValue) || isNaN(lowValue) || isNaN(highValue)) {
40
+ return null;
41
+ }
42
+
43
+ const xPos = api.coord([xValue, 0])[0];
44
+ const openPos = api.coord([xValue, openValue])[1];
45
+ const closePos = api.coord([xValue, closeValue])[1];
46
+ const lowPos = api.coord([xValue, lowValue])[1];
47
+ const highPos = api.coord([xValue, highValue])[1];
48
+
49
+ const barWidth = api.size([1, 0])[0] * 0.6;
50
+
51
+ if (isCandle) {
52
+ // Classic candlestick rendering
53
+ const bodyTop = Math.min(openPos, closePos);
54
+ const bodyBottom = Math.max(openPos, closePos);
55
+ const bodyHeight = Math.abs(closePos - openPos);
56
+
57
+ return {
58
+ type: 'group',
59
+ children: [
60
+ // Upper wick
61
+ {
62
+ type: 'line',
63
+ shape: {
64
+ x1: xPos,
65
+ y1: highPos,
66
+ x2: xPos,
67
+ y2: bodyTop,
68
+ },
69
+ style: {
70
+ stroke: wickColor,
71
+ lineWidth: 1,
72
+ },
73
+ },
74
+ // Lower wick
75
+ {
76
+ type: 'line',
77
+ shape: {
78
+ x1: xPos,
79
+ y1: bodyBottom,
80
+ x2: xPos,
81
+ y2: lowPos,
82
+ },
83
+ style: {
84
+ stroke: wickColor,
85
+ lineWidth: 1,
86
+ },
87
+ },
88
+ // Body
89
+ {
90
+ type: 'rect',
91
+ shape: {
92
+ x: xPos - barWidth / 2,
93
+ y: bodyTop,
94
+ width: barWidth,
95
+ height: bodyHeight || 1, // Minimum height for doji
96
+ },
97
+ style: {
98
+ fill: color,
99
+ stroke: borderColor,
100
+ lineWidth: 1,
101
+ },
102
+ },
103
+ ],
104
+ };
105
+ } else {
106
+ // Bar style (OHLC bar)
107
+ const tickWidth = barWidth * 0.5;
108
+
109
+ return {
110
+ type: 'group',
111
+ children: [
112
+ // Vertical line (low to high)
113
+ {
114
+ type: 'line',
115
+ shape: {
116
+ x1: xPos,
117
+ y1: lowPos,
118
+ x2: xPos,
119
+ y2: highPos,
120
+ },
121
+ style: {
122
+ stroke: color,
123
+ lineWidth: 1,
124
+ },
125
+ },
126
+ // Open tick (left)
127
+ {
128
+ type: 'line',
129
+ shape: {
130
+ x1: xPos - tickWidth,
131
+ y1: openPos,
132
+ x2: xPos,
133
+ y2: openPos,
134
+ },
135
+ style: {
136
+ stroke: color,
137
+ lineWidth: 1,
138
+ },
139
+ },
140
+ // Close tick (right)
141
+ {
142
+ type: 'line',
143
+ shape: {
144
+ x1: xPos,
145
+ y1: closePos,
146
+ x2: xPos + tickWidth,
147
+ y2: closePos,
148
+ },
149
+ style: {
150
+ stroke: color,
151
+ lineWidth: 1,
152
+ },
153
+ },
154
+ ],
155
+ };
156
+ }
157
+ },
158
+ data: ohlcData,
159
+ };
160
+ }
161
+ }
@@ -0,0 +1,54 @@
1
+ import { SeriesRenderer, RenderContext } from './SeriesRenderer';
2
+ import { textToBase64Image } from '../../utils/CanvasUtils';
3
+
4
+ export class ScatterRenderer implements SeriesRenderer {
5
+ render(context: RenderContext): any {
6
+ const { seriesName, xAxisIndex, yAxisIndex, dataArray, colorArray, plotOptions } = context;
7
+ const defaultColor = '#2962ff';
8
+ const style = plotOptions.style; // 'circles', 'cross', 'char'
9
+
10
+ // Special handling for invisible 'char' style
11
+ if (style === 'char') {
12
+ return {
13
+ name: seriesName,
14
+ type: 'scatter',
15
+ xAxisIndex: xAxisIndex,
16
+ yAxisIndex: yAxisIndex,
17
+ symbolSize: 0, // Invisible
18
+ data: dataArray.map((val, i) => ({
19
+ value: [i, val],
20
+ itemStyle: { opacity: 0 },
21
+ })),
22
+ silent: true, // No interaction
23
+ };
24
+ }
25
+
26
+ const scatterData = dataArray
27
+ .map((val, i) => {
28
+ if (val === null) return null;
29
+ const pointColor = colorArray[i] || plotOptions.color || defaultColor;
30
+ const item: any = {
31
+ value: [i, val],
32
+ itemStyle: { color: pointColor },
33
+ };
34
+
35
+ if (style === 'cross') {
36
+ item.symbol = `image://${textToBase64Image('+', pointColor, '24px')}`;
37
+ item.symbolSize = 16;
38
+ } else {
39
+ item.symbol = 'circle';
40
+ item.symbolSize = 6;
41
+ }
42
+ return item;
43
+ })
44
+ .filter((item) => item !== null);
45
+
46
+ return {
47
+ name: seriesName,
48
+ type: 'scatter',
49
+ xAxisIndex: xAxisIndex,
50
+ yAxisIndex: yAxisIndex,
51
+ data: scatterData,
52
+ };
53
+ }
54
+ }
@@ -0,0 +1,20 @@
1
+ import { IndicatorPlot, OHLCV } from '../../types';
2
+
3
+ export interface RenderContext {
4
+ seriesName: string;
5
+ xAxisIndex: number;
6
+ yAxisIndex: number;
7
+ dataArray: any[];
8
+ colorArray: any[];
9
+ optionsArray: any[];
10
+ plotOptions: any;
11
+ candlestickData?: OHLCV[]; // For shape positioning
12
+ plotDataArrays?: Map<string, number[]>; // For fill plots
13
+ indicatorId?: string;
14
+ plotName?: string;
15
+ indicator?: any; // Reference to parent indicator object if needed
16
+ }
17
+
18
+ export interface SeriesRenderer {
19
+ render(context: RenderContext): any;
20
+ }
@@ -0,0 +1,121 @@
1
+ import { SeriesRenderer, RenderContext } from './SeriesRenderer';
2
+ import { ShapeUtils } from '../../utils/ShapeUtils';
3
+
4
+ export class ShapeRenderer implements SeriesRenderer {
5
+ render(context: RenderContext): any {
6
+ const { seriesName, xAxisIndex, yAxisIndex, dataArray, colorArray, optionsArray, plotOptions, candlestickData } = context;
7
+ const defaultColor = '#2962ff';
8
+
9
+ const shapeData = dataArray
10
+ .map((val, i) => {
11
+ // Merge global options with per-point options to get location first
12
+ const pointOpts = optionsArray[i] || {};
13
+ const globalOpts = plotOptions;
14
+ const location = pointOpts.location || globalOpts.location || 'absolute';
15
+
16
+ // For location="absolute", always draw the shape (ignore value)
17
+ // For other locations, only draw if value is truthy (TradingView behavior)
18
+ if (location !== 'absolute' && !val) {
19
+ return null;
20
+ }
21
+
22
+ // If we get here and val is null/undefined, it means location is absolute
23
+ // In that case, we still need a valid value for positioning
24
+ // Use the value if it exists, otherwise we'd need a fallback
25
+ // But in TradingView, absolute location still expects a value for Y position
26
+ if (val === null || val === undefined) {
27
+ return null; // Can't plot without a Y coordinate
28
+ }
29
+
30
+ const color = pointOpts.color || globalOpts.color || defaultColor;
31
+ const shape = pointOpts.shape || globalOpts.shape || 'circle';
32
+ const size = pointOpts.size || globalOpts.size || 'normal';
33
+ const text = pointOpts.text || globalOpts.text;
34
+ const textColor = pointOpts.textcolor || globalOpts.textcolor || 'white';
35
+
36
+ // NEW: Get width and height
37
+ const width = pointOpts.width || globalOpts.width;
38
+ const height = pointOpts.height || globalOpts.height;
39
+
40
+ // Positioning based on location
41
+ let yValue = val; // Default to absolute value
42
+ let symbolOffset: (string | number)[] = [0, 0];
43
+
44
+ if (location === 'abovebar') {
45
+ // Shape above the candle
46
+ if (candlestickData && candlestickData[i]) {
47
+ yValue = candlestickData[i].high;
48
+ }
49
+ symbolOffset = [0, '-150%']; // Shift up
50
+ } else if (location === 'belowbar') {
51
+ // Shape below the candle
52
+ if (candlestickData && candlestickData[i]) {
53
+ yValue = candlestickData[i].low;
54
+ }
55
+ symbolOffset = [0, '150%']; // Shift down
56
+ } else if (location === 'top') {
57
+ // Shape at top of chart - we need to use a very high value
58
+ // This would require knowing the y-axis max, which we don't have here easily
59
+ // For now, use a placeholder approach - might need to calculate from data
60
+ // Or we can use a percentage of the viewport? ECharts doesn't support that directly in scatter.
61
+ // Best approach: use a large multiplier of current value or track max
62
+ // Simplified: use coordinate system max (will need enhancement)
63
+ yValue = val; // For now, keep absolute - would need axis max
64
+ symbolOffset = [0, 0];
65
+ } else if (location === 'bottom') {
66
+ // Shape at bottom of chart
67
+ yValue = val; // For now, keep absolute - would need axis min
68
+ symbolOffset = [0, 0];
69
+ }
70
+
71
+ const symbol = ShapeUtils.getShapeSymbol(shape);
72
+ const symbolSize = ShapeUtils.getShapeSize(size, width, height);
73
+ const rotate = ShapeUtils.getShapeRotation(shape);
74
+
75
+ // Special handling for labelup/down sizing - they contain text so they should be larger
76
+ let finalSize: number | number[] = symbolSize;
77
+ if (shape.includes('label')) {
78
+ // If custom size, scale it up for labels
79
+ if (Array.isArray(symbolSize)) {
80
+ finalSize = [symbolSize[0] * 2.5, symbolSize[1] * 2.5];
81
+ } else {
82
+ finalSize = symbolSize * 2.5;
83
+ }
84
+ }
85
+
86
+ // Get label configuration based on location
87
+ const labelConfig = ShapeUtils.getLabelConfig(shape, location);
88
+
89
+ const item: any = {
90
+ value: [i, yValue],
91
+ symbol: symbol,
92
+ symbolSize: finalSize,
93
+ symbolRotate: rotate,
94
+ symbolOffset: symbolOffset,
95
+ itemStyle: {
96
+ color: color,
97
+ },
98
+ label: {
99
+ show: !!text,
100
+ position: labelConfig.position,
101
+ distance: labelConfig.distance,
102
+ formatter: text,
103
+ color: textColor,
104
+ fontSize: 10,
105
+ fontWeight: 'bold',
106
+ },
107
+ };
108
+
109
+ return item;
110
+ })
111
+ .filter((item) => item !== null);
112
+
113
+ return {
114
+ name: seriesName,
115
+ type: 'scatter',
116
+ xAxisIndex: xAxisIndex,
117
+ yAxisIndex: yAxisIndex,
118
+ data: shapeData,
119
+ };
120
+ }
121
+ }
@@ -0,0 +1,39 @@
1
+ import { SeriesRenderer, RenderContext } from './SeriesRenderer';
2
+
3
+ export class StepRenderer implements SeriesRenderer {
4
+ render(context: RenderContext): any {
5
+ const { seriesName, xAxisIndex, yAxisIndex, dataArray, colorArray, plotOptions } = context;
6
+ const defaultColor = '#2962ff';
7
+
8
+ return {
9
+ name: seriesName,
10
+ type: 'custom',
11
+ xAxisIndex: xAxisIndex,
12
+ yAxisIndex: yAxisIndex,
13
+ renderItem: (params: any, api: any) => {
14
+ const x = api.value(0);
15
+ const y = api.value(1);
16
+ if (isNaN(y) || y === null) return;
17
+
18
+ const coords = api.coord([x, y]);
19
+ const width = api.size([1, 0])[0];
20
+
21
+ return {
22
+ type: 'line',
23
+ shape: {
24
+ x1: coords[0] - width / 2,
25
+ y1: coords[1],
26
+ x2: coords[0] + width / 2,
27
+ y2: coords[1],
28
+ },
29
+ style: {
30
+ stroke: colorArray[params.dataIndex] || plotOptions.color || defaultColor,
31
+ lineWidth: plotOptions.linewidth || 1,
32
+ },
33
+ silent: true,
34
+ };
35
+ },
36
+ data: dataArray.map((val, i) => [i, val]),
37
+ };
38
+ }
39
+ }