@qfo/qfchart 0.7.2 → 0.7.3
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/index.d.ts +5 -0
- package/dist/qfchart.min.browser.js +16 -16
- package/dist/qfchart.min.es.js +16 -16
- package/package.json +1 -1
- package/src/QFChart.ts +172 -10
- package/src/components/LayoutManager.ts +34 -2
- package/src/components/SeriesBuilder.ts +18 -1
- package/src/components/TableOverlayRenderer.ts +38 -15
- package/src/components/renderers/BoxRenderer.ts +21 -21
- package/src/components/renderers/DrawingLineRenderer.ts +12 -16
- package/src/components/renderers/HistogramRenderer.ts +67 -20
- package/src/components/renderers/LinefillRenderer.ts +4 -12
- package/src/components/renderers/PolylineRenderer.ts +6 -13
|
@@ -1,20 +1,67 @@
|
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
+
const histbase: number = plotOptions.histbase ?? 0;
|
|
8
|
+
const isColumns = plotOptions.style === 'columns';
|
|
9
|
+
const linewidth: number = plotOptions.linewidth ?? 1;
|
|
10
|
+
|
|
11
|
+
// Build data array: [index, value, color]
|
|
12
|
+
const customData = dataArray.map((val: number | null, i: number) => {
|
|
13
|
+
if (val === null || val === undefined || (typeof val === 'number' && isNaN(val))) return null;
|
|
14
|
+
return [i, val, colorArray[i] || plotOptions.color || defaultColor];
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
name: seriesName,
|
|
19
|
+
type: 'custom',
|
|
20
|
+
xAxisIndex: xAxisIndex,
|
|
21
|
+
yAxisIndex: yAxisIndex,
|
|
22
|
+
renderItem: (params: any, api: any) => {
|
|
23
|
+
const idx = api.value(0);
|
|
24
|
+
const value = api.value(1);
|
|
25
|
+
const color = api.value(2);
|
|
26
|
+
|
|
27
|
+
if (value === null || value === undefined || isNaN(value)) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const basePos = api.coord([idx, histbase]);
|
|
32
|
+
const valuePos = api.coord([idx, value]);
|
|
33
|
+
const candleWidth = api.size([1, 0])[0];
|
|
34
|
+
|
|
35
|
+
// Columns: thick bars (60% of candle width)
|
|
36
|
+
// Histogram: thin bars — scale with linewidth (like TradingView)
|
|
37
|
+
let barWidth: number;
|
|
38
|
+
if (isColumns) {
|
|
39
|
+
barWidth = candleWidth * 0.6;
|
|
40
|
+
} else {
|
|
41
|
+
// Thin line-like bars: linewidth controls pixel width (min 1px)
|
|
42
|
+
barWidth = Math.max(1, linewidth);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const x = basePos[0];
|
|
46
|
+
const yBase = basePos[1];
|
|
47
|
+
const yValue = valuePos[1];
|
|
48
|
+
const top = Math.min(yBase, yValue);
|
|
49
|
+
const height = Math.abs(yValue - yBase);
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
type: 'rect',
|
|
53
|
+
shape: {
|
|
54
|
+
x: x - barWidth / 2,
|
|
55
|
+
y: top,
|
|
56
|
+
width: barWidth,
|
|
57
|
+
height: height || 1, // Minimum 1px for zero-height bars
|
|
58
|
+
},
|
|
59
|
+
style: {
|
|
60
|
+
fill: color,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
data: customData.filter((d: any) => d !== null),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -37,20 +37,12 @@ export class LinefillRenderer implements SeriesRenderer {
|
|
|
37
37
|
return { name: seriesName, type: 'custom', xAxisIndex, yAxisIndex, data: [], silent: true };
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
// Compute y-range for axis scaling
|
|
41
|
-
let yMin = Infinity, yMax = -Infinity;
|
|
42
|
-
for (const lf of fillObjects) {
|
|
43
|
-
for (const y of [lf.line1.y1, lf.line1.y2, lf.line2.y1, lf.line2.y2]) {
|
|
44
|
-
if (y < yMin) yMin = y;
|
|
45
|
-
if (y > yMax) yMax = y;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
40
|
// Use a SINGLE data entry spanning the full x-range so renderItem is always called.
|
|
50
41
|
// ECharts filters a data item only when ALL its x-dimensions are on the same side
|
|
51
42
|
// of the visible window. With dims 0=0 and 1=lastBar the item always straddles
|
|
52
43
|
// the viewport, so renderItem fires exactly once regardless of scroll position.
|
|
53
|
-
//
|
|
44
|
+
// Note: We do NOT encode y-dimensions — drawing objects should not influence the
|
|
45
|
+
// y-axis auto-scaling.
|
|
54
46
|
const totalBars = (context.candlestickData?.length || 0) + offset;
|
|
55
47
|
const lastBarIndex = Math.max(0, totalBars - 1);
|
|
56
48
|
|
|
@@ -103,9 +95,9 @@ export class LinefillRenderer implements SeriesRenderer {
|
|
|
103
95
|
|
|
104
96
|
return { type: 'group', children };
|
|
105
97
|
},
|
|
106
|
-
data: [[0, lastBarIndex
|
|
98
|
+
data: [[0, lastBarIndex]],
|
|
107
99
|
clip: true,
|
|
108
|
-
encode: { x: [0, 1]
|
|
100
|
+
encode: { x: [0, 1] },
|
|
109
101
|
z: 10, // Behind lines (z=15) but above other elements
|
|
110
102
|
silent: true,
|
|
111
103
|
emphasis: { disabled: true },
|
|
@@ -34,21 +34,12 @@ export class PolylineRenderer implements SeriesRenderer {
|
|
|
34
34
|
return { name: seriesName, type: 'custom', xAxisIndex, yAxisIndex, data: [], silent: true };
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
// Compute y-range across all polylines for axis scaling
|
|
38
|
-
let yMin = Infinity, yMax = -Infinity;
|
|
39
|
-
for (const pl of polyObjects) {
|
|
40
|
-
for (const pt of pl.points) {
|
|
41
|
-
const p = pt.price ?? 0;
|
|
42
|
-
if (p < yMin) yMin = p;
|
|
43
|
-
if (p > yMax) yMax = p;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
37
|
// Use a SINGLE data entry spanning the full x-range so renderItem is always called.
|
|
48
38
|
// ECharts filters a data item only when ALL its x-dimensions are on the same side
|
|
49
39
|
// of the visible window. With dims 0=0 and 1=lastBar the item always straddles
|
|
50
40
|
// the viewport, so renderItem fires exactly once regardless of scroll position.
|
|
51
|
-
//
|
|
41
|
+
// Note: We do NOT encode y-dimensions — drawing objects should not influence the
|
|
42
|
+
// y-axis auto-scaling.
|
|
52
43
|
const totalBars = (context.candlestickData?.length || 0) + offset;
|
|
53
44
|
const lastBarIndex = Math.max(0, totalBars - 1);
|
|
54
45
|
|
|
@@ -126,9 +117,11 @@ export class PolylineRenderer implements SeriesRenderer {
|
|
|
126
117
|
|
|
127
118
|
return { type: 'group', children };
|
|
128
119
|
},
|
|
129
|
-
data: [[0, lastBarIndex
|
|
120
|
+
data: [[0, lastBarIndex]],
|
|
130
121
|
clip: true,
|
|
131
|
-
encode: { x: [0, 1]
|
|
122
|
+
encode: { x: [0, 1] },
|
|
123
|
+
// Prevent ECharts visual system from overriding element colors with palette
|
|
124
|
+
itemStyle: { color: 'transparent', borderColor: 'transparent' },
|
|
132
125
|
z: 12,
|
|
133
126
|
silent: true,
|
|
134
127
|
emphasis: { disabled: true },
|