@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
|
@@ -1,248 +1,297 @@
|
|
|
1
|
-
import { ChartContext, DrawingElement, DataCoordinate } from
|
|
2
|
-
import * as echarts from
|
|
3
|
-
|
|
4
|
-
export class DrawingEditor {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
1
|
+
import { ChartContext, DrawingElement, DataCoordinate } from '../types';
|
|
2
|
+
import * as echarts from 'echarts';
|
|
3
|
+
|
|
4
|
+
export class DrawingEditor {
|
|
5
|
+
private context: ChartContext;
|
|
6
|
+
private isEditing: boolean = false;
|
|
7
|
+
private currentDrawing: DrawingElement | null = null;
|
|
8
|
+
private editingPointIndex: number | null = null;
|
|
9
|
+
private zr: any;
|
|
10
|
+
|
|
11
|
+
// Temporary ZRender elements for visual feedback during drag
|
|
12
|
+
private editGroup: any = null;
|
|
13
|
+
private editLines: any[] = [];
|
|
14
|
+
private editPoints: any[] = [];
|
|
15
|
+
|
|
16
|
+
private isMovingShape: boolean = false;
|
|
17
|
+
private dragStart: { x: number; y: number } | null = null;
|
|
18
|
+
private initialPixelPoints: { x: number; y: number }[] = [];
|
|
19
|
+
|
|
20
|
+
constructor(context: ChartContext) {
|
|
21
|
+
this.context = context;
|
|
22
|
+
this.zr = this.context.getChart().getZr();
|
|
23
|
+
this.bindEvents();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private bindEvents() {
|
|
27
|
+
this.context.events.on('drawing:point:mousedown', this.onPointMouseDown);
|
|
28
|
+
this.context.events.on('drawing:mousedown', this.onDrawingMouseDown);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private onDrawingMouseDown = (payload: { id: string; x: number; y: number }) => {
|
|
32
|
+
if (this.isEditing) return;
|
|
33
|
+
|
|
34
|
+
const drawing = this.context.getDrawing(payload.id);
|
|
35
|
+
if (!drawing) return;
|
|
36
|
+
|
|
37
|
+
this.isEditing = true;
|
|
38
|
+
this.isMovingShape = true;
|
|
39
|
+
this.currentDrawing = JSON.parse(JSON.stringify(drawing));
|
|
40
|
+
this.dragStart = { x: payload.x, y: payload.y };
|
|
41
|
+
|
|
42
|
+
// Capture initial pixel positions for all points
|
|
43
|
+
this.initialPixelPoints = drawing.points.map((p) => {
|
|
44
|
+
const pixel = this.context.coordinateConversion.dataToPixel(p);
|
|
45
|
+
return pixel ? { x: pixel.x, y: pixel.y } : { x: 0, y: 0 };
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
this.context.lockChart();
|
|
49
|
+
this.createEditGraphic();
|
|
50
|
+
|
|
51
|
+
this.zr.on('mousemove', this.onMouseMove);
|
|
52
|
+
this.zr.on('mouseup', this.onMouseUp);
|
|
53
|
+
// Safety net: catch mouseup outside the canvas
|
|
54
|
+
window.addEventListener('mouseup', this.onWindowMouseUp);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
private onPointMouseDown = (payload: { id: string; pointIndex: number }) => {
|
|
58
|
+
if (this.isEditing) return;
|
|
59
|
+
|
|
60
|
+
const drawing = this.context.getDrawing(payload.id);
|
|
61
|
+
if (!drawing) return;
|
|
62
|
+
|
|
63
|
+
this.isEditing = true;
|
|
64
|
+
this.currentDrawing = JSON.parse(JSON.stringify(drawing));
|
|
65
|
+
this.editingPointIndex = payload.pointIndex;
|
|
66
|
+
|
|
67
|
+
// Capture initial pixel positions for all points
|
|
68
|
+
this.initialPixelPoints = drawing.points.map((p) => {
|
|
69
|
+
const pixel = this.context.coordinateConversion.dataToPixel(p);
|
|
70
|
+
return pixel ? { x: pixel.x, y: pixel.y } : { x: 0, y: 0 };
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
this.context.lockChart();
|
|
74
|
+
this.createEditGraphic();
|
|
75
|
+
|
|
76
|
+
this.zr.on('mousemove', this.onMouseMove);
|
|
77
|
+
this.zr.on('mouseup', this.onMouseUp);
|
|
78
|
+
// Safety net: catch mouseup outside the canvas
|
|
79
|
+
window.addEventListener('mouseup', this.onWindowMouseUp);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
private createEditGraphic() {
|
|
83
|
+
if (!this.currentDrawing) return;
|
|
84
|
+
|
|
85
|
+
this.editGroup = new echarts.graphic.Group();
|
|
86
|
+
this.editLines = [];
|
|
87
|
+
this.editPoints = [];
|
|
88
|
+
|
|
89
|
+
const pixelPts = this.currentDrawing.points.map((p) => {
|
|
90
|
+
const px = this.context.coordinateConversion.dataToPixel(p);
|
|
91
|
+
return px ? { x: px.x, y: px.y } : null;
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
if (pixelPts.some((p) => !p)) return;
|
|
95
|
+
const pts = pixelPts as { x: number; y: number }[];
|
|
96
|
+
|
|
97
|
+
// Connect consecutive points with dashed lines
|
|
98
|
+
for (let i = 0; i < pts.length - 1; i++) {
|
|
99
|
+
const line = new echarts.graphic.Line({
|
|
100
|
+
shape: { x1: pts[i].x, y1: pts[i].y, x2: pts[i + 1].x, y2: pts[i + 1].y },
|
|
101
|
+
style: {
|
|
102
|
+
stroke: this.currentDrawing.style?.color || '#3b82f6',
|
|
103
|
+
lineWidth: this.currentDrawing.style?.lineWidth || 2,
|
|
104
|
+
lineDash: [4, 4],
|
|
105
|
+
},
|
|
106
|
+
silent: true,
|
|
107
|
+
});
|
|
108
|
+
this.editLines.push(line);
|
|
109
|
+
this.editGroup.add(line);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Control point circles for each point
|
|
113
|
+
for (let i = 0; i < pts.length; i++) {
|
|
114
|
+
const circle = new echarts.graphic.Circle({
|
|
115
|
+
shape: { cx: pts[i].x, cy: pts[i].y, r: 5 },
|
|
116
|
+
style: { fill: '#fff', stroke: '#3b82f6', lineWidth: 2 },
|
|
117
|
+
z: 1000,
|
|
118
|
+
});
|
|
119
|
+
this.editPoints.push(circle);
|
|
120
|
+
this.editGroup.add(circle);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
this.zr.add(this.editGroup);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private onMouseMove = (e: any) => {
|
|
127
|
+
if (!this.isEditing || !this.currentDrawing) return;
|
|
128
|
+
|
|
129
|
+
const x = e.offsetX;
|
|
130
|
+
const y = e.offsetY;
|
|
131
|
+
|
|
132
|
+
if (this.isMovingShape && this.dragStart) {
|
|
133
|
+
const dx = x - this.dragStart.x;
|
|
134
|
+
const dy = y - this.dragStart.y;
|
|
135
|
+
|
|
136
|
+
// Compute new positions for all points
|
|
137
|
+
const newPts = this.initialPixelPoints.map((p) => ({
|
|
138
|
+
x: p.x + dx,
|
|
139
|
+
y: p.y + dy,
|
|
140
|
+
}));
|
|
141
|
+
|
|
142
|
+
// Update lines
|
|
143
|
+
for (let i = 0; i < this.editLines.length; i++) {
|
|
144
|
+
this.editLines[i].setShape({
|
|
145
|
+
x1: newPts[i].x,
|
|
146
|
+
y1: newPts[i].y,
|
|
147
|
+
x2: newPts[i + 1].x,
|
|
148
|
+
y2: newPts[i + 1].y,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Update point circles
|
|
153
|
+
for (let i = 0; i < this.editPoints.length; i++) {
|
|
154
|
+
this.editPoints[i].setShape({ cx: newPts[i].x, cy: newPts[i].y });
|
|
155
|
+
}
|
|
156
|
+
} else if (this.editingPointIndex !== null) {
|
|
157
|
+
// Compute new positions: only the dragged point moves
|
|
158
|
+
const newPts = this.initialPixelPoints.map((p) => ({ x: p.x, y: p.y }));
|
|
159
|
+
newPts[this.editingPointIndex] = { x, y };
|
|
160
|
+
|
|
161
|
+
// Update lines connected to this point
|
|
162
|
+
for (let i = 0; i < this.editLines.length; i++) {
|
|
163
|
+
this.editLines[i].setShape({
|
|
164
|
+
x1: newPts[i].x,
|
|
165
|
+
y1: newPts[i].y,
|
|
166
|
+
x2: newPts[i + 1].x,
|
|
167
|
+
y2: newPts[i + 1].y,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Update the dragged point circle
|
|
172
|
+
this.editPoints[this.editingPointIndex].setShape({ cx: x, cy: y });
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
private onMouseUp = (e: any) => {
|
|
177
|
+
if (!this.isEditing) return;
|
|
178
|
+
this.finishEditing(e.offsetX, e.offsetY);
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Safety net: catches mouseup when the cursor leaves the canvas area.
|
|
183
|
+
* Uses the last known pixel positions to compute the final drop location
|
|
184
|
+
* relative to the chart container.
|
|
185
|
+
*/
|
|
186
|
+
private onWindowMouseUp = (e: MouseEvent) => {
|
|
187
|
+
if (!this.isEditing) return;
|
|
188
|
+
|
|
189
|
+
// Convert page coordinates to offset relative to the chart canvas
|
|
190
|
+
const dom = this.zr.dom as HTMLElement;
|
|
191
|
+
if (dom) {
|
|
192
|
+
const rect = dom.getBoundingClientRect();
|
|
193
|
+
const offsetX = e.clientX - rect.left;
|
|
194
|
+
const offsetY = e.clientY - rect.top;
|
|
195
|
+
this.finishEditing(offsetX, offsetY);
|
|
196
|
+
} else {
|
|
197
|
+
// Fallback: just clean up without committing the move
|
|
198
|
+
this.finishEditing(this.dragStart?.x ?? 0, this.dragStart?.y ?? 0);
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Convert pixel to data, falling back to the drawing's known pane
|
|
204
|
+
* when the point is outside the grid (e.g., dragged beyond viewport).
|
|
205
|
+
* Uses convertFromPixel with the specific gridIndex directly, bypassing
|
|
206
|
+
* the containPixel check that would return null for out-of-bounds points.
|
|
207
|
+
*/
|
|
208
|
+
private pixelToDataForPane(x: number, y: number, paneIndex: number): DataCoordinate | null {
|
|
209
|
+
// First try the normal conversion (respects pane boundaries)
|
|
210
|
+
const normal = this.context.coordinateConversion.pixelToData({ x, y });
|
|
211
|
+
if (normal) return normal;
|
|
212
|
+
|
|
213
|
+
// Fallback: force conversion using the drawing's known pane
|
|
214
|
+
try {
|
|
215
|
+
const chart = this.context.getChart();
|
|
216
|
+
const p = chart.convertFromPixel({ gridIndex: paneIndex }, [x, y]);
|
|
217
|
+
if (p) {
|
|
218
|
+
// We need the dataIndexOffset — read from the chart's xAxis data length
|
|
219
|
+
const option = chart.getOption() as any;
|
|
220
|
+
const xAxisData = option?.xAxis?.[paneIndex]?.data;
|
|
221
|
+
const marketData = this.context.getMarketData();
|
|
222
|
+
const dataIndexOffset = xAxisData ? Math.round((xAxisData.length - marketData.length) / 2) : 0;
|
|
223
|
+
return { timeIndex: Math.round(p[0]) - dataIndexOffset, value: p[1], paneIndex };
|
|
224
|
+
}
|
|
225
|
+
} catch (_) {
|
|
226
|
+
// Ignore conversion errors
|
|
227
|
+
}
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
private finishEditing(finalX: number, finalY: number) {
|
|
232
|
+
if (!this.currentDrawing) {
|
|
233
|
+
this.cleanup();
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const paneIndex = this.currentDrawing.paneIndex || 0;
|
|
238
|
+
|
|
239
|
+
if (this.isMovingShape && this.dragStart) {
|
|
240
|
+
const dx = finalX - this.dragStart.x;
|
|
241
|
+
const dy = finalY - this.dragStart.y;
|
|
242
|
+
|
|
243
|
+
// Update all points using pane-aware conversion
|
|
244
|
+
const newPoints = this.initialPixelPoints.map((p) =>
|
|
245
|
+
this.pixelToDataForPane(p.x + dx, p.y + dy, paneIndex),
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
if (newPoints.every((p) => p !== null)) {
|
|
249
|
+
for (let i = 0; i < newPoints.length; i++) {
|
|
250
|
+
this.currentDrawing.points[i] = newPoints[i]!;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (newPoints[0]?.paneIndex !== undefined) {
|
|
254
|
+
this.currentDrawing.paneIndex = newPoints[0].paneIndex;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
this.context.updateDrawing(this.currentDrawing);
|
|
258
|
+
}
|
|
259
|
+
} else if (this.editingPointIndex !== null) {
|
|
260
|
+
const newData = this.pixelToDataForPane(finalX, finalY, paneIndex);
|
|
261
|
+
|
|
262
|
+
if (newData) {
|
|
263
|
+
this.currentDrawing.points[this.editingPointIndex] = newData;
|
|
264
|
+
|
|
265
|
+
if (this.editingPointIndex === 0 && newData.paneIndex !== undefined) {
|
|
266
|
+
this.currentDrawing.paneIndex = newData.paneIndex;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
this.context.updateDrawing(this.currentDrawing);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
this.cleanup();
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
private cleanup() {
|
|
277
|
+
this.isEditing = false;
|
|
278
|
+
this.isMovingShape = false;
|
|
279
|
+
this.dragStart = null;
|
|
280
|
+
this.initialPixelPoints = [];
|
|
281
|
+
this.currentDrawing = null;
|
|
282
|
+
this.editingPointIndex = null;
|
|
283
|
+
this.editLines = [];
|
|
284
|
+
this.editPoints = [];
|
|
285
|
+
|
|
286
|
+
if (this.editGroup) {
|
|
287
|
+
this.zr.remove(this.editGroup);
|
|
288
|
+
this.editGroup = null;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
this.zr.off('mousemove', this.onMouseMove);
|
|
292
|
+
this.zr.off('mouseup', this.onMouseUp);
|
|
293
|
+
window.removeEventListener('mouseup', this.onWindowMouseUp);
|
|
294
|
+
|
|
295
|
+
this.context.unlockChart();
|
|
296
|
+
}
|
|
297
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { DrawingRenderer } from '../types';
|
|
2
|
+
|
|
3
|
+
export class DrawingRendererRegistry {
|
|
4
|
+
private renderers = new Map<string, DrawingRenderer>();
|
|
5
|
+
|
|
6
|
+
register(renderer: DrawingRenderer): void {
|
|
7
|
+
this.renderers.set(renderer.type, renderer);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
get(type: string): DrawingRenderer | undefined {
|
|
11
|
+
return this.renderers.get(type);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -8,7 +8,7 @@ export class GraphicBuilder {
|
|
|
8
8
|
onToggle: (id: string, action?: 'collapse' | 'maximize' | 'fullscreen') => void,
|
|
9
9
|
isMainCollapsed: boolean = false,
|
|
10
10
|
maximizedPaneId: string | null = null,
|
|
11
|
-
overlayIndicators: { id: string; titleColor?: string }[] = []
|
|
11
|
+
overlayIndicators: { id: string; titleColor?: string }[] = [],
|
|
12
12
|
): any[] {
|
|
13
13
|
const graphic: any[] = [];
|
|
14
14
|
const pixelToPercent = layout.pixelToPercent;
|
|
@@ -29,7 +29,7 @@ export class GraphicBuilder {
|
|
|
29
29
|
top: mainPaneTop + titleTopMargin + '%',
|
|
30
30
|
z: 10,
|
|
31
31
|
style: {
|
|
32
|
-
text: options.title || '
|
|
32
|
+
text: options.title || '',
|
|
33
33
|
fill: options.titleColor || '#fff',
|
|
34
34
|
font: `bold 16px ${options.fontFamily || 'sans-serif'}`,
|
|
35
35
|
textVerticalAlign: 'top',
|