@qfo/qfchart 0.8.0 → 0.8.2
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 +524 -12
- package/dist/qfchart.min.browser.js +34 -18
- package/dist/qfchart.min.es.js +34 -18
- package/package.json +1 -1
- package/src/QFChart.ts +109 -272
- package/src/components/AbstractPlugin.ts +234 -104
- package/src/components/DrawingEditor.ts +297 -248
- package/src/components/DrawingRendererRegistry.ts +13 -0
- package/src/components/GraphicBuilder.ts +2 -2
- package/src/components/LayoutManager.ts +92 -52
- package/src/components/SeriesBuilder.ts +10 -10
- package/src/components/TooltipFormatter.ts +1 -1
- package/src/index.ts +25 -6
- package/src/plugins/ABCDPatternTool/ABCDPatternDrawingRenderer.ts +112 -0
- package/src/plugins/ABCDPatternTool/ABCDPatternTool.ts +136 -0
- package/src/plugins/ABCDPatternTool/index.ts +2 -0
- package/src/plugins/CrossLineTool/CrossLineDrawingRenderer.ts +49 -0
- package/src/plugins/CrossLineTool/CrossLineTool.ts +52 -0
- package/src/plugins/CrossLineTool/index.ts +2 -0
- package/src/plugins/CypherPatternTool/CypherPatternDrawingRenderer.ts +80 -0
- package/src/plugins/CypherPatternTool/CypherPatternTool.ts +84 -0
- package/src/plugins/CypherPatternTool/index.ts +2 -0
- package/src/plugins/ExtendedLineTool/ExtendedLineDrawingRenderer.ts +73 -0
- package/src/plugins/ExtendedLineTool/ExtendedLineTool.ts +173 -0
- package/src/plugins/ExtendedLineTool/index.ts +2 -0
- package/src/plugins/FibSpeedResistanceFanTool/FibSpeedResistanceFanDrawingRenderer.ts +163 -0
- package/src/plugins/FibSpeedResistanceFanTool/FibSpeedResistanceFanTool.ts +210 -0
- package/src/plugins/FibSpeedResistanceFanTool/index.ts +2 -0
- package/src/plugins/FibTrendExtensionTool/FibTrendExtensionDrawingRenderer.ts +141 -0
- package/src/plugins/FibTrendExtensionTool/FibTrendExtensionTool.ts +188 -0
- package/src/plugins/FibTrendExtensionTool/index.ts +2 -0
- package/src/plugins/FibonacciChannelTool/FibonacciChannelDrawingRenderer.ts +128 -0
- package/src/plugins/FibonacciChannelTool/FibonacciChannelTool.ts +231 -0
- package/src/plugins/FibonacciChannelTool/index.ts +2 -0
- package/src/plugins/FibonacciTool/FibonacciDrawingRenderer.ts +107 -0
- package/src/plugins/{FibonacciTool.ts → FibonacciTool/FibonacciTool.ts} +195 -192
- package/src/plugins/FibonacciTool/index.ts +2 -0
- package/src/plugins/HeadAndShouldersTool/HeadAndShouldersDrawingRenderer.ts +95 -0
- package/src/plugins/HeadAndShouldersTool/HeadAndShouldersTool.ts +97 -0
- package/src/plugins/HeadAndShouldersTool/index.ts +2 -0
- package/src/plugins/HorizontalLineTool/HorizontalLineDrawingRenderer.ts +54 -0
- package/src/plugins/HorizontalLineTool/HorizontalLineTool.ts +52 -0
- package/src/plugins/HorizontalLineTool/index.ts +2 -0
- package/src/plugins/HorizontalRayTool/HorizontalRayDrawingRenderer.ts +34 -0
- package/src/plugins/HorizontalRayTool/HorizontalRayTool.ts +52 -0
- package/src/plugins/HorizontalRayTool/index.ts +2 -0
- package/src/plugins/InfoLineTool/InfoLineDrawingRenderer.ts +72 -0
- package/src/plugins/InfoLineTool/InfoLineTool.ts +130 -0
- package/src/plugins/InfoLineTool/index.ts +2 -0
- package/src/plugins/LineTool/LineDrawingRenderer.ts +49 -0
- package/src/plugins/{LineTool.ts → LineTool/LineTool.ts} +161 -190
- package/src/plugins/LineTool/index.ts +2 -0
- package/src/plugins/{MeasureTool.ts → MeasureTool/MeasureTool.ts} +324 -344
- package/src/plugins/MeasureTool/index.ts +1 -0
- package/src/plugins/RayTool/RayDrawingRenderer.ts +69 -0
- package/src/plugins/RayTool/RayTool.ts +162 -0
- package/src/plugins/RayTool/index.ts +2 -0
- package/src/plugins/ThreeDrivesPatternTool/ThreeDrivesPatternDrawingRenderer.ts +106 -0
- package/src/plugins/ThreeDrivesPatternTool/ThreeDrivesPatternTool.ts +98 -0
- package/src/plugins/ThreeDrivesPatternTool/index.ts +2 -0
- package/src/plugins/ToolGroup.ts +211 -0
- package/src/plugins/TrendAngleTool/TrendAngleDrawingRenderer.ts +87 -0
- package/src/plugins/TrendAngleTool/TrendAngleTool.ts +176 -0
- package/src/plugins/TrendAngleTool/index.ts +2 -0
- package/src/plugins/TrianglePatternTool/TrianglePatternDrawingRenderer.ts +107 -0
- package/src/plugins/TrianglePatternTool/TrianglePatternTool.ts +98 -0
- package/src/plugins/TrianglePatternTool/index.ts +2 -0
- package/src/plugins/VerticalLineTool/VerticalLineDrawingRenderer.ts +35 -0
- package/src/plugins/VerticalLineTool/VerticalLineTool.ts +52 -0
- package/src/plugins/VerticalLineTool/index.ts +2 -0
- package/src/plugins/XABCDPatternTool/XABCDPatternDrawingRenderer.ts +178 -0
- package/src/plugins/XABCDPatternTool/XABCDPatternTool.ts +213 -0
- package/src/plugins/XABCDPatternTool/index.ts +2 -0
- package/src/types.ts +39 -11
package/package.json
CHANGED
package/src/QFChart.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as echarts from 'echarts';
|
|
2
|
-
import { OHLCV, IndicatorPlot, QFChartOptions, Indicator as IndicatorInterface, ChartContext, Plugin } from './types';
|
|
2
|
+
import { OHLCV, IndicatorPlot, QFChartOptions, Indicator as IndicatorInterface, ChartContext, Plugin, DrawingRenderer } from './types';
|
|
3
3
|
import { Indicator } from './components/Indicator';
|
|
4
4
|
import { LayoutManager, LayoutResult, PaneBoundary } from './components/LayoutManager';
|
|
5
5
|
import { SeriesBuilder } from './components/SeriesBuilder';
|
|
@@ -7,6 +7,7 @@ import { GraphicBuilder } from './components/GraphicBuilder';
|
|
|
7
7
|
import { TooltipFormatter } from './components/TooltipFormatter';
|
|
8
8
|
import { PluginManager } from './components/PluginManager';
|
|
9
9
|
import { DrawingEditor } from './components/DrawingEditor';
|
|
10
|
+
import { DrawingRendererRegistry } from './components/DrawingRendererRegistry';
|
|
10
11
|
import { EventBus } from './utils/EventBus';
|
|
11
12
|
import { AxisUtils } from './utils/AxisUtils';
|
|
12
13
|
import { TableOverlayRenderer } from './components/TableOverlayRenderer';
|
|
@@ -29,6 +30,7 @@ export class QFChart implements ChartContext {
|
|
|
29
30
|
|
|
30
31
|
// Drawing System
|
|
31
32
|
private drawings: import('./types').DrawingElement[] = [];
|
|
33
|
+
private drawingRenderers: DrawingRendererRegistry = new DrawingRendererRegistry();
|
|
32
34
|
|
|
33
35
|
public coordinateConversion = {
|
|
34
36
|
pixelToData: (point: { x: number; y: number }) => {
|
|
@@ -117,7 +119,7 @@ export class QFChart implements ChartContext {
|
|
|
117
119
|
constructor(container: HTMLElement, options: QFChartOptions = {}) {
|
|
118
120
|
this.rootContainer = container;
|
|
119
121
|
this.options = {
|
|
120
|
-
title:
|
|
122
|
+
title: undefined,
|
|
121
123
|
height: '600px',
|
|
122
124
|
backgroundColor: '#1e293b',
|
|
123
125
|
upColor: '#00da3c',
|
|
@@ -214,6 +216,8 @@ export class QFChart implements ChartContext {
|
|
|
214
216
|
this.pluginManager = new PluginManager(this, this.toolbarContainer);
|
|
215
217
|
this.drawingEditor = new DrawingEditor(this);
|
|
216
218
|
|
|
219
|
+
|
|
220
|
+
|
|
217
221
|
// Bind global chart/ZRender events to the EventBus
|
|
218
222
|
this.chart.on('dataZoom', (params: any) => {
|
|
219
223
|
this.events.emit('chart:dataZoom', params);
|
|
@@ -507,8 +511,8 @@ export class QFChart implements ChartContext {
|
|
|
507
511
|
});
|
|
508
512
|
// Set cursor
|
|
509
513
|
this.chart.getZr().setCursorStyle('move');
|
|
510
|
-
} else if (info.targetName?.startsWith('point')) {
|
|
511
|
-
const pointIdx = info.targetName
|
|
514
|
+
} else if (info.targetName?.startsWith('point-')) {
|
|
515
|
+
const pointIdx = parseInt(info.targetName.split('-')[1]) || 0;
|
|
512
516
|
this.events.emit('drawing:point:hover', {
|
|
513
517
|
id: info.drawing.id,
|
|
514
518
|
pointIndex: pointIdx,
|
|
@@ -549,8 +553,8 @@ export class QFChart implements ChartContext {
|
|
|
549
553
|
|
|
550
554
|
if (info.targetName === 'line') {
|
|
551
555
|
this.events.emit('drawing:mouseout', { id: info.drawing.id });
|
|
552
|
-
} else if (info.targetName?.startsWith('point')) {
|
|
553
|
-
const pointIdx = info.targetName
|
|
556
|
+
} else if (info.targetName?.startsWith('point-')) {
|
|
557
|
+
const pointIdx = parseInt(info.targetName.split('-')[1]) || 0;
|
|
554
558
|
this.events.emit('drawing:point:mouseout', {
|
|
555
559
|
id: info.drawing.id,
|
|
556
560
|
pointIndex: pointIdx,
|
|
@@ -573,8 +577,8 @@ export class QFChart implements ChartContext {
|
|
|
573
577
|
x,
|
|
574
578
|
y,
|
|
575
579
|
});
|
|
576
|
-
} else if (info.targetName?.startsWith('point')) {
|
|
577
|
-
const pointIdx = info.targetName
|
|
580
|
+
} else if (info.targetName?.startsWith('point-')) {
|
|
581
|
+
const pointIdx = parseInt(info.targetName.split('-')[1]) || 0;
|
|
578
582
|
this.events.emit('drawing:point:mousedown', {
|
|
579
583
|
id: info.drawing.id,
|
|
580
584
|
pointIndex: pointIdx,
|
|
@@ -597,8 +601,8 @@ export class QFChart implements ChartContext {
|
|
|
597
601
|
|
|
598
602
|
if (info.targetName === 'line') {
|
|
599
603
|
this.events.emit('drawing:click', { id: info.drawing.id });
|
|
600
|
-
} else if (info.targetName?.startsWith('point')) {
|
|
601
|
-
const pointIdx = info.targetName
|
|
604
|
+
} else if (info.targetName?.startsWith('point-')) {
|
|
605
|
+
const pointIdx = parseInt(info.targetName.split('-')[1]) || 0;
|
|
602
606
|
this.events.emit('drawing:point:click', {
|
|
603
607
|
id: info.drawing.id,
|
|
604
608
|
pointIndex: pointIdx,
|
|
@@ -686,6 +690,64 @@ export class QFChart implements ChartContext {
|
|
|
686
690
|
this.pluginManager.register(plugin);
|
|
687
691
|
}
|
|
688
692
|
|
|
693
|
+
public registerDrawingRenderer(renderer: DrawingRenderer): void {
|
|
694
|
+
this.drawingRenderers.register(renderer);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
public snapToCandle(point: { x: number; y: number }): { x: number; y: number } {
|
|
698
|
+
// Find which pane the point is in
|
|
699
|
+
const dataCoord = this.coordinateConversion.pixelToData(point);
|
|
700
|
+
if (!dataCoord) return point;
|
|
701
|
+
|
|
702
|
+
const paneIndex = dataCoord.paneIndex || 0;
|
|
703
|
+
// Only snap on the main pane (candlestick data)
|
|
704
|
+
if (paneIndex !== 0) return point;
|
|
705
|
+
|
|
706
|
+
// Get the nearest candle by time index
|
|
707
|
+
const realIndex = Math.round(dataCoord.timeIndex);
|
|
708
|
+
if (realIndex < 0 || realIndex >= this.marketData.length) return point;
|
|
709
|
+
|
|
710
|
+
const candle = this.marketData[realIndex];
|
|
711
|
+
if (!candle) return point;
|
|
712
|
+
|
|
713
|
+
// Snap X to the exact candle center
|
|
714
|
+
const snappedX = this.chart.convertToPixel(
|
|
715
|
+
{ gridIndex: paneIndex },
|
|
716
|
+
[realIndex + this.dataIndexOffset, candle.close],
|
|
717
|
+
);
|
|
718
|
+
if (!snappedX) return point;
|
|
719
|
+
const snapPxX = snappedX[0];
|
|
720
|
+
|
|
721
|
+
// Find closest OHLC value by Y distance
|
|
722
|
+
const ohlc = [candle.open, candle.high, candle.low, candle.close];
|
|
723
|
+
let bestValue = ohlc[0];
|
|
724
|
+
let bestDist = Infinity;
|
|
725
|
+
|
|
726
|
+
for (const val of ohlc) {
|
|
727
|
+
const px = this.chart.convertToPixel(
|
|
728
|
+
{ gridIndex: paneIndex },
|
|
729
|
+
[realIndex + this.dataIndexOffset, val],
|
|
730
|
+
);
|
|
731
|
+
if (px) {
|
|
732
|
+
const dist = Math.abs(px[1] - point.y);
|
|
733
|
+
if (dist < bestDist) {
|
|
734
|
+
bestDist = dist;
|
|
735
|
+
bestValue = val;
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
const snappedY = this.chart.convertToPixel(
|
|
741
|
+
{ gridIndex: paneIndex },
|
|
742
|
+
[realIndex + this.dataIndexOffset, bestValue],
|
|
743
|
+
);
|
|
744
|
+
|
|
745
|
+
return {
|
|
746
|
+
x: snapPxX,
|
|
747
|
+
y: snappedY ? snappedY[1] : point.y,
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
|
|
689
751
|
// --- Drawing System ---
|
|
690
752
|
|
|
691
753
|
public addDrawing(drawing: import('./types').DrawingElement): void {
|
|
@@ -1049,7 +1111,7 @@ export class QFChart implements ChartContext {
|
|
|
1049
1111
|
this.chart.setOption({
|
|
1050
1112
|
series: [
|
|
1051
1113
|
{
|
|
1052
|
-
|
|
1114
|
+
id: '__candlestick__',
|
|
1053
1115
|
markLine: {
|
|
1054
1116
|
data: [
|
|
1055
1117
|
{
|
|
@@ -1335,22 +1397,22 @@ export class QFChart implements ChartContext {
|
|
|
1335
1397
|
});
|
|
1336
1398
|
drawingsByPane.forEach((paneDrawings) => {
|
|
1337
1399
|
drawingSeriesUpdates.push({
|
|
1338
|
-
data: paneDrawings.map((d) =>
|
|
1339
|
-
|
|
1340
|
-
d.points
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1400
|
+
data: paneDrawings.map((d) => {
|
|
1401
|
+
const row: number[] = [];
|
|
1402
|
+
d.points.forEach((p) => {
|
|
1403
|
+
row.push(p.timeIndex + this.dataIndexOffset, p.value);
|
|
1404
|
+
});
|
|
1405
|
+
return row;
|
|
1406
|
+
}),
|
|
1344
1407
|
});
|
|
1345
1408
|
});
|
|
1346
1409
|
|
|
1347
1410
|
// 6. Merge update — preserves drag/interaction state
|
|
1348
1411
|
const updateOption: any = {
|
|
1349
1412
|
xAxis: currentOption.xAxis.map(() => ({ data: categoryData })),
|
|
1350
|
-
dataZoom: [
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
],
|
|
1413
|
+
dataZoom: (currentOption.dataZoom || []).map(() => ({
|
|
1414
|
+
start: newStart, end: newEnd,
|
|
1415
|
+
})),
|
|
1354
1416
|
series: [
|
|
1355
1417
|
{ data: coloredCandlestickData, markLine: candlestickSeries.markLine },
|
|
1356
1418
|
...indicatorSeries.map((s) => {
|
|
@@ -1593,260 +1655,35 @@ export class QFChart implements ChartContext {
|
|
|
1593
1655
|
const drawing = drawings[params.dataIndex];
|
|
1594
1656
|
if (!drawing) return;
|
|
1595
1657
|
|
|
1596
|
-
const
|
|
1597
|
-
|
|
1658
|
+
const renderer = this.drawingRenderers.get(drawing.type);
|
|
1659
|
+
if (!renderer) return;
|
|
1598
1660
|
|
|
1599
|
-
if (!start || !end) return;
|
|
1600
|
-
|
|
1601
|
-
// Convert real data indices to padded space for ECharts rendering
|
|
1602
1661
|
const drawingOffset = this.dataIndexOffset;
|
|
1603
|
-
const
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
name: 'line',
|
|
1615
|
-
shape: {
|
|
1616
|
-
x1: p1[0],
|
|
1617
|
-
y1: p1[1],
|
|
1618
|
-
x2: p2[0],
|
|
1619
|
-
y2: p2[1],
|
|
1620
|
-
},
|
|
1621
|
-
style: {
|
|
1622
|
-
stroke: drawing.style?.color || '#3b82f6',
|
|
1623
|
-
lineWidth: drawing.style?.lineWidth || 2,
|
|
1624
|
-
},
|
|
1625
|
-
},
|
|
1626
|
-
{
|
|
1627
|
-
type: 'circle',
|
|
1628
|
-
name: 'point-start',
|
|
1629
|
-
shape: { cx: p1[0], cy: p1[1], r: 4 },
|
|
1630
|
-
style: {
|
|
1631
|
-
fill: '#fff',
|
|
1632
|
-
stroke: drawing.style?.color || '#3b82f6',
|
|
1633
|
-
lineWidth: 1,
|
|
1634
|
-
opacity: isSelected ? 1 : 0, // Show if selected
|
|
1635
|
-
},
|
|
1636
|
-
},
|
|
1637
|
-
{
|
|
1638
|
-
type: 'circle',
|
|
1639
|
-
name: 'point-end',
|
|
1640
|
-
shape: { cx: p2[0], cy: p2[1], r: 4 },
|
|
1641
|
-
style: {
|
|
1642
|
-
fill: '#fff',
|
|
1643
|
-
stroke: drawing.style?.color || '#3b82f6',
|
|
1644
|
-
lineWidth: 1,
|
|
1645
|
-
opacity: isSelected ? 1 : 0, // Show if selected
|
|
1646
|
-
},
|
|
1647
|
-
},
|
|
1648
|
-
],
|
|
1649
|
-
};
|
|
1650
|
-
} else if (drawing.type === 'fibonacci') {
|
|
1651
|
-
const x1 = p1[0];
|
|
1652
|
-
const y1 = p1[1];
|
|
1653
|
-
const x2 = p2[0];
|
|
1654
|
-
const y2 = p2[1];
|
|
1655
|
-
|
|
1656
|
-
const startX = Math.min(x1, x2);
|
|
1657
|
-
const endX = Math.max(x1, x2);
|
|
1658
|
-
const width = endX - startX;
|
|
1659
|
-
const diffY = y2 - y1;
|
|
1660
|
-
|
|
1661
|
-
const levels = [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1];
|
|
1662
|
-
const colors = ['#787b86', '#f44336', '#ff9800', '#4caf50', '#2196f3', '#00bcd4', '#787b86'];
|
|
1663
|
-
|
|
1664
|
-
const children: any[] = [];
|
|
1665
|
-
|
|
1666
|
-
// 1. Diagonal Line
|
|
1667
|
-
children.push({
|
|
1668
|
-
type: 'line',
|
|
1669
|
-
name: 'line', // Use 'line' name to enable dragging logic in DrawingEditor
|
|
1670
|
-
shape: { x1, y1, x2, y2 },
|
|
1671
|
-
style: {
|
|
1672
|
-
stroke: '#999',
|
|
1673
|
-
lineWidth: 1,
|
|
1674
|
-
lineDash: [4, 4],
|
|
1675
|
-
},
|
|
1676
|
-
});
|
|
1677
|
-
|
|
1678
|
-
// 2. Control Points (invisible by default)
|
|
1679
|
-
children.push({
|
|
1680
|
-
type: 'circle',
|
|
1681
|
-
name: 'point-start',
|
|
1682
|
-
shape: { cx: x1, cy: y1, r: 4 },
|
|
1683
|
-
style: {
|
|
1684
|
-
fill: '#fff',
|
|
1685
|
-
stroke: drawing.style?.color || '#3b82f6',
|
|
1686
|
-
lineWidth: 1,
|
|
1687
|
-
opacity: isSelected ? 1 : 0,
|
|
1688
|
-
},
|
|
1689
|
-
z: 100, // Ensure on top
|
|
1690
|
-
});
|
|
1691
|
-
children.push({
|
|
1692
|
-
type: 'circle',
|
|
1693
|
-
name: 'point-end',
|
|
1694
|
-
shape: { cx: x2, cy: y2, r: 4 },
|
|
1695
|
-
style: {
|
|
1696
|
-
fill: '#fff',
|
|
1697
|
-
stroke: drawing.style?.color || '#3b82f6',
|
|
1698
|
-
lineWidth: 1,
|
|
1699
|
-
opacity: isSelected ? 1 : 0,
|
|
1700
|
-
},
|
|
1701
|
-
z: 100,
|
|
1702
|
-
});
|
|
1703
|
-
|
|
1704
|
-
// 3. Levels and Backgrounds
|
|
1705
|
-
levels.forEach((level, index) => {
|
|
1706
|
-
const levelY = y2 - diffY * level;
|
|
1707
|
-
const color = colors[index % colors.length];
|
|
1708
|
-
|
|
1709
|
-
// Horizontal Line
|
|
1710
|
-
children.push({
|
|
1711
|
-
type: 'line',
|
|
1712
|
-
name: 'fib-line', // distinct name, maybe we don't want to drag by clicking these lines? or yes? 'line' triggers drag. 'fib-line' won't unless we update logic.
|
|
1713
|
-
// The user asked for "fib levels between start and end".
|
|
1714
|
-
shape: { x1: startX, y1: levelY, x2: endX, y2: levelY },
|
|
1715
|
-
style: { stroke: color, lineWidth: 1 },
|
|
1716
|
-
silent: true, // Make internal lines silent so clicks pass to background/diagonal?
|
|
1717
|
-
});
|
|
1718
|
-
|
|
1719
|
-
const startVal = drawing.points[0].value;
|
|
1720
|
-
const endVal = drawing.points[1].value;
|
|
1721
|
-
const valDiff = endVal - startVal;
|
|
1722
|
-
const price = endVal - valDiff * level;
|
|
1723
|
-
|
|
1724
|
-
children.push({
|
|
1725
|
-
type: 'text',
|
|
1726
|
-
style: {
|
|
1727
|
-
text: `${level} (${price.toFixed(2)})`,
|
|
1728
|
-
x: startX + 5,
|
|
1729
|
-
y: levelY - 10,
|
|
1730
|
-
fill: color,
|
|
1731
|
-
fontSize: 10,
|
|
1732
|
-
},
|
|
1733
|
-
silent: true,
|
|
1734
|
-
});
|
|
1735
|
-
|
|
1736
|
-
// Background
|
|
1737
|
-
if (index < levels.length - 1) {
|
|
1738
|
-
const nextLevel = levels[index + 1];
|
|
1739
|
-
const nextY = y2 - diffY * nextLevel;
|
|
1740
|
-
const rectH = Math.abs(nextY - levelY);
|
|
1741
|
-
const rectY = Math.min(levelY, nextY);
|
|
1742
|
-
|
|
1743
|
-
children.push({
|
|
1744
|
-
type: 'rect',
|
|
1745
|
-
shape: { x: startX, y: rectY, width, height: rectH },
|
|
1746
|
-
style: {
|
|
1747
|
-
fill: colors[(index + 1) % colors.length],
|
|
1748
|
-
opacity: 0.1,
|
|
1749
|
-
},
|
|
1750
|
-
silent: true, // Let clicks pass through?
|
|
1751
|
-
});
|
|
1752
|
-
}
|
|
1753
|
-
});
|
|
1754
|
-
|
|
1755
|
-
const backgrounds: any[] = [];
|
|
1756
|
-
const linesAndText: any[] = [];
|
|
1757
|
-
|
|
1758
|
-
levels.forEach((level, index) => {
|
|
1759
|
-
const levelY = y2 - diffY * level;
|
|
1760
|
-
const color = colors[index % colors.length];
|
|
1761
|
-
|
|
1762
|
-
linesAndText.push({
|
|
1763
|
-
type: 'line',
|
|
1764
|
-
shape: { x1: startX, y1: levelY, x2: endX, y2: levelY },
|
|
1765
|
-
style: { stroke: color, lineWidth: 1 },
|
|
1766
|
-
silent: true,
|
|
1767
|
-
});
|
|
1768
|
-
|
|
1769
|
-
const startVal = drawing.points[0].value;
|
|
1770
|
-
const endVal = drawing.points[1].value;
|
|
1771
|
-
const valDiff = endVal - startVal;
|
|
1772
|
-
const price = endVal - valDiff * level;
|
|
1773
|
-
|
|
1774
|
-
linesAndText.push({
|
|
1775
|
-
type: 'text',
|
|
1776
|
-
style: {
|
|
1777
|
-
text: `${level} (${price.toFixed(2)})`,
|
|
1778
|
-
x: startX + 5,
|
|
1779
|
-
y: levelY - 10,
|
|
1780
|
-
fill: color,
|
|
1781
|
-
fontSize: 10,
|
|
1782
|
-
},
|
|
1783
|
-
silent: true,
|
|
1784
|
-
});
|
|
1785
|
-
|
|
1786
|
-
if (index < levels.length - 1) {
|
|
1787
|
-
const nextLevel = levels[index + 1];
|
|
1788
|
-
const nextY = y2 - diffY * nextLevel;
|
|
1789
|
-
const rectH = Math.abs(nextY - levelY);
|
|
1790
|
-
const rectY = Math.min(levelY, nextY);
|
|
1791
|
-
|
|
1792
|
-
backgrounds.push({
|
|
1793
|
-
type: 'rect',
|
|
1794
|
-
name: 'line', // Enable dragging by clicking background!
|
|
1795
|
-
shape: { x: startX, y: rectY, width, height: rectH },
|
|
1796
|
-
style: {
|
|
1797
|
-
fill: colors[(index + 1) % colors.length],
|
|
1798
|
-
opacity: 0.1,
|
|
1799
|
-
},
|
|
1800
|
-
});
|
|
1801
|
-
}
|
|
1802
|
-
});
|
|
1803
|
-
|
|
1804
|
-
return {
|
|
1805
|
-
type: 'group',
|
|
1806
|
-
children: [
|
|
1807
|
-
...backgrounds,
|
|
1808
|
-
...linesAndText,
|
|
1809
|
-
{
|
|
1810
|
-
type: 'line',
|
|
1811
|
-
name: 'line',
|
|
1812
|
-
shape: { x1, y1, x2, y2 },
|
|
1813
|
-
style: { stroke: '#999', lineWidth: 1, lineDash: [4, 4] },
|
|
1814
|
-
},
|
|
1815
|
-
{
|
|
1816
|
-
type: 'circle',
|
|
1817
|
-
name: 'point-start',
|
|
1818
|
-
shape: { cx: x1, cy: y1, r: 4 },
|
|
1819
|
-
style: {
|
|
1820
|
-
fill: '#fff',
|
|
1821
|
-
stroke: drawing.style?.color || '#3b82f6',
|
|
1822
|
-
lineWidth: 1,
|
|
1823
|
-
opacity: isSelected ? 1 : 0,
|
|
1824
|
-
},
|
|
1825
|
-
z: 100,
|
|
1826
|
-
},
|
|
1827
|
-
{
|
|
1828
|
-
type: 'circle',
|
|
1829
|
-
name: 'point-end',
|
|
1830
|
-
shape: { cx: x2, cy: y2, r: 4 },
|
|
1831
|
-
style: {
|
|
1832
|
-
fill: '#fff',
|
|
1833
|
-
stroke: drawing.style?.color || '#3b82f6',
|
|
1834
|
-
lineWidth: 1,
|
|
1835
|
-
opacity: isSelected ? 1 : 0,
|
|
1836
|
-
},
|
|
1837
|
-
z: 100,
|
|
1838
|
-
},
|
|
1839
|
-
],
|
|
1840
|
-
};
|
|
1841
|
-
}
|
|
1662
|
+
const pixelPoints = drawing.points.map(
|
|
1663
|
+
(p) => api.coord([p.timeIndex + drawingOffset, p.value]) as [number, number],
|
|
1664
|
+
);
|
|
1665
|
+
|
|
1666
|
+
return renderer.render({
|
|
1667
|
+
drawing,
|
|
1668
|
+
pixelPoints,
|
|
1669
|
+
isSelected: drawing.id === this.selectedDrawingId,
|
|
1670
|
+
api,
|
|
1671
|
+
coordSys: params.coordSys,
|
|
1672
|
+
});
|
|
1842
1673
|
},
|
|
1843
|
-
data: drawings.map((d) =>
|
|
1844
|
-
|
|
1845
|
-
d.points
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1674
|
+
data: drawings.map((d) => {
|
|
1675
|
+
const row: number[] = [];
|
|
1676
|
+
d.points.forEach((p) => {
|
|
1677
|
+
row.push(p.timeIndex + this.dataIndexOffset, p.value);
|
|
1678
|
+
});
|
|
1679
|
+
return row;
|
|
1680
|
+
}),
|
|
1681
|
+
encode: (() => {
|
|
1682
|
+
const maxPoints = drawings.reduce((max, d) => Math.max(max, d.points.length), 0);
|
|
1683
|
+
const xDims = Array.from({ length: maxPoints }, (_, i) => i * 2);
|
|
1684
|
+
const yDims = Array.from({ length: maxPoints }, (_, i) => i * 2 + 1);
|
|
1685
|
+
return { x: xDims, y: yDims };
|
|
1686
|
+
})(),
|
|
1850
1687
|
z: 100,
|
|
1851
1688
|
silent: false,
|
|
1852
1689
|
});
|