@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.
Files changed (74) hide show
  1. package/dist/index.d.ts +524 -12
  2. package/dist/qfchart.min.browser.js +34 -18
  3. package/dist/qfchart.min.es.js +34 -18
  4. package/package.json +1 -1
  5. package/src/QFChart.ts +109 -272
  6. package/src/components/AbstractPlugin.ts +234 -104
  7. package/src/components/DrawingEditor.ts +297 -248
  8. package/src/components/DrawingRendererRegistry.ts +13 -0
  9. package/src/components/GraphicBuilder.ts +2 -2
  10. package/src/components/LayoutManager.ts +92 -52
  11. package/src/components/SeriesBuilder.ts +10 -10
  12. package/src/components/TooltipFormatter.ts +1 -1
  13. package/src/index.ts +25 -6
  14. package/src/plugins/ABCDPatternTool/ABCDPatternDrawingRenderer.ts +112 -0
  15. package/src/plugins/ABCDPatternTool/ABCDPatternTool.ts +136 -0
  16. package/src/plugins/ABCDPatternTool/index.ts +2 -0
  17. package/src/plugins/CrossLineTool/CrossLineDrawingRenderer.ts +49 -0
  18. package/src/plugins/CrossLineTool/CrossLineTool.ts +52 -0
  19. package/src/plugins/CrossLineTool/index.ts +2 -0
  20. package/src/plugins/CypherPatternTool/CypherPatternDrawingRenderer.ts +80 -0
  21. package/src/plugins/CypherPatternTool/CypherPatternTool.ts +84 -0
  22. package/src/plugins/CypherPatternTool/index.ts +2 -0
  23. package/src/plugins/ExtendedLineTool/ExtendedLineDrawingRenderer.ts +73 -0
  24. package/src/plugins/ExtendedLineTool/ExtendedLineTool.ts +173 -0
  25. package/src/plugins/ExtendedLineTool/index.ts +2 -0
  26. package/src/plugins/FibSpeedResistanceFanTool/FibSpeedResistanceFanDrawingRenderer.ts +163 -0
  27. package/src/plugins/FibSpeedResistanceFanTool/FibSpeedResistanceFanTool.ts +210 -0
  28. package/src/plugins/FibSpeedResistanceFanTool/index.ts +2 -0
  29. package/src/plugins/FibTrendExtensionTool/FibTrendExtensionDrawingRenderer.ts +141 -0
  30. package/src/plugins/FibTrendExtensionTool/FibTrendExtensionTool.ts +188 -0
  31. package/src/plugins/FibTrendExtensionTool/index.ts +2 -0
  32. package/src/plugins/FibonacciChannelTool/FibonacciChannelDrawingRenderer.ts +128 -0
  33. package/src/plugins/FibonacciChannelTool/FibonacciChannelTool.ts +231 -0
  34. package/src/plugins/FibonacciChannelTool/index.ts +2 -0
  35. package/src/plugins/FibonacciTool/FibonacciDrawingRenderer.ts +107 -0
  36. package/src/plugins/{FibonacciTool.ts → FibonacciTool/FibonacciTool.ts} +195 -192
  37. package/src/plugins/FibonacciTool/index.ts +2 -0
  38. package/src/plugins/HeadAndShouldersTool/HeadAndShouldersDrawingRenderer.ts +95 -0
  39. package/src/plugins/HeadAndShouldersTool/HeadAndShouldersTool.ts +97 -0
  40. package/src/plugins/HeadAndShouldersTool/index.ts +2 -0
  41. package/src/plugins/HorizontalLineTool/HorizontalLineDrawingRenderer.ts +54 -0
  42. package/src/plugins/HorizontalLineTool/HorizontalLineTool.ts +52 -0
  43. package/src/plugins/HorizontalLineTool/index.ts +2 -0
  44. package/src/plugins/HorizontalRayTool/HorizontalRayDrawingRenderer.ts +34 -0
  45. package/src/plugins/HorizontalRayTool/HorizontalRayTool.ts +52 -0
  46. package/src/plugins/HorizontalRayTool/index.ts +2 -0
  47. package/src/plugins/InfoLineTool/InfoLineDrawingRenderer.ts +72 -0
  48. package/src/plugins/InfoLineTool/InfoLineTool.ts +130 -0
  49. package/src/plugins/InfoLineTool/index.ts +2 -0
  50. package/src/plugins/LineTool/LineDrawingRenderer.ts +49 -0
  51. package/src/plugins/{LineTool.ts → LineTool/LineTool.ts} +161 -190
  52. package/src/plugins/LineTool/index.ts +2 -0
  53. package/src/plugins/{MeasureTool.ts → MeasureTool/MeasureTool.ts} +324 -344
  54. package/src/plugins/MeasureTool/index.ts +1 -0
  55. package/src/plugins/RayTool/RayDrawingRenderer.ts +69 -0
  56. package/src/plugins/RayTool/RayTool.ts +162 -0
  57. package/src/plugins/RayTool/index.ts +2 -0
  58. package/src/plugins/ThreeDrivesPatternTool/ThreeDrivesPatternDrawingRenderer.ts +106 -0
  59. package/src/plugins/ThreeDrivesPatternTool/ThreeDrivesPatternTool.ts +98 -0
  60. package/src/plugins/ThreeDrivesPatternTool/index.ts +2 -0
  61. package/src/plugins/ToolGroup.ts +211 -0
  62. package/src/plugins/TrendAngleTool/TrendAngleDrawingRenderer.ts +87 -0
  63. package/src/plugins/TrendAngleTool/TrendAngleTool.ts +176 -0
  64. package/src/plugins/TrendAngleTool/index.ts +2 -0
  65. package/src/plugins/TrianglePatternTool/TrianglePatternDrawingRenderer.ts +107 -0
  66. package/src/plugins/TrianglePatternTool/TrianglePatternTool.ts +98 -0
  67. package/src/plugins/TrianglePatternTool/index.ts +2 -0
  68. package/src/plugins/VerticalLineTool/VerticalLineDrawingRenderer.ts +35 -0
  69. package/src/plugins/VerticalLineTool/VerticalLineTool.ts +52 -0
  70. package/src/plugins/VerticalLineTool/index.ts +2 -0
  71. package/src/plugins/XABCDPatternTool/XABCDPatternDrawingRenderer.ts +178 -0
  72. package/src/plugins/XABCDPatternTool/XABCDPatternTool.ts +213 -0
  73. package/src/plugins/XABCDPatternTool/index.ts +2 -0
  74. package/src/types.ts +39 -11
@@ -0,0 +1,178 @@
1
+ import { DrawingRenderer, DrawingRenderContext } from '../../types';
2
+
3
+ const LABELS = ['X', 'A', 'B', 'C', 'D'];
4
+ const LEG_COLORS = ['#2196f3', '#ff9800', '#4caf50', '#f44336'];
5
+ const FILL_COLOR_1 = 'rgba(33, 150, 243, 0.08)';
6
+ const FILL_COLOR_2 = 'rgba(244, 67, 54, 0.08)';
7
+
8
+ export class XABCDPatternDrawingRenderer implements DrawingRenderer {
9
+ type = 'xabcd_pattern';
10
+
11
+ render(ctx: DrawingRenderContext): any {
12
+ const { drawing, pixelPoints, isSelected } = ctx;
13
+ const color = drawing.style?.color || '#3b82f6';
14
+
15
+ if (pixelPoints.length < 2) return;
16
+
17
+ const children: any[] = [];
18
+
19
+ // Fill triangles XAB and BCD
20
+ if (pixelPoints.length >= 3) {
21
+ children.push({
22
+ type: 'polygon',
23
+ name: 'line',
24
+ shape: {
25
+ points: pixelPoints.slice(0, 3).map(([x, y]) => [x, y]),
26
+ },
27
+ style: { fill: FILL_COLOR_1, opacity: 1 },
28
+ });
29
+ }
30
+ if (pixelPoints.length >= 5) {
31
+ children.push({
32
+ type: 'polygon',
33
+ name: 'line',
34
+ shape: {
35
+ points: pixelPoints.slice(2, 5).map(([x, y]) => [x, y]),
36
+ },
37
+ style: { fill: FILL_COLOR_2, opacity: 1 },
38
+ });
39
+ }
40
+
41
+ // Leg lines X→A→B→C→D
42
+ for (let i = 0; i < pixelPoints.length - 1; i++) {
43
+ const [x1, y1] = pixelPoints[i];
44
+ const [x2, y2] = pixelPoints[i + 1];
45
+ const legColor = LEG_COLORS[i % LEG_COLORS.length];
46
+
47
+ children.push({
48
+ type: 'line',
49
+ name: 'line',
50
+ shape: { x1, y1, x2, y2 },
51
+ style: { stroke: legColor, lineWidth: drawing.style?.lineWidth || 2 },
52
+ });
53
+ }
54
+
55
+ // Dashed connector lines: X→B, A→C, B→D (retrace references)
56
+ const connectors: [number, number][] = [[0, 2], [1, 3], [2, 4]];
57
+ for (const [from, to] of connectors) {
58
+ if (from < pixelPoints.length && to < pixelPoints.length) {
59
+ const [x1, y1] = pixelPoints[from];
60
+ const [x2, y2] = pixelPoints[to];
61
+ children.push({
62
+ type: 'line',
63
+ shape: { x1, y1, x2, y2 },
64
+ style: { stroke: '#555', lineWidth: 1, lineDash: [4, 4] },
65
+ silent: true,
66
+ });
67
+ }
68
+ }
69
+
70
+ // Fibonacci ratio labels on legs
71
+ if (drawing.points.length >= 3) {
72
+ // AB/XA ratio
73
+ const xa = Math.abs(drawing.points[1].value - drawing.points[0].value);
74
+ const ab = Math.abs(drawing.points[2].value - drawing.points[1].value);
75
+ if (xa !== 0) {
76
+ const ratio = (ab / xa).toFixed(3);
77
+ const mx = (pixelPoints[1][0] + pixelPoints[2][0]) / 2;
78
+ const my = (pixelPoints[1][1] + pixelPoints[2][1]) / 2;
79
+ children.push({
80
+ type: 'text',
81
+ style: { text: ratio, x: mx + 8, y: my, fill: '#ff9800', fontSize: 10 },
82
+ silent: true,
83
+ });
84
+ }
85
+ }
86
+ if (drawing.points.length >= 4) {
87
+ // BC/AB ratio
88
+ const ab = Math.abs(drawing.points[2].value - drawing.points[1].value);
89
+ const bc = Math.abs(drawing.points[3].value - drawing.points[2].value);
90
+ if (ab !== 0) {
91
+ const ratio = (bc / ab).toFixed(3);
92
+ const mx = (pixelPoints[2][0] + pixelPoints[3][0]) / 2;
93
+ const my = (pixelPoints[2][1] + pixelPoints[3][1]) / 2;
94
+ children.push({
95
+ type: 'text',
96
+ style: { text: ratio, x: mx + 8, y: my, fill: '#4caf50', fontSize: 10 },
97
+ silent: true,
98
+ });
99
+ }
100
+ }
101
+ if (drawing.points.length >= 5) {
102
+ // CD/BC ratio
103
+ const bc = Math.abs(drawing.points[3].value - drawing.points[2].value);
104
+ const cd = Math.abs(drawing.points[4].value - drawing.points[3].value);
105
+ if (bc !== 0) {
106
+ const ratio = (cd / bc).toFixed(3);
107
+ const mx = (pixelPoints[3][0] + pixelPoints[4][0]) / 2;
108
+ const my = (pixelPoints[3][1] + pixelPoints[4][1]) / 2;
109
+ children.push({
110
+ type: 'text',
111
+ style: { text: ratio, x: mx + 8, y: my, fill: '#f44336', fontSize: 10 },
112
+ silent: true,
113
+ });
114
+ }
115
+
116
+ // XA/AD ratio (overall retracement)
117
+ const xa = Math.abs(drawing.points[1].value - drawing.points[0].value);
118
+ const ad = Math.abs(drawing.points[4].value - drawing.points[1].value);
119
+ if (xa !== 0) {
120
+ const ratio = (ad / xa).toFixed(3);
121
+ // Place near D point
122
+ const [dx, dy] = pixelPoints[4];
123
+ children.push({
124
+ type: 'text',
125
+ style: { text: `AD/XA: ${ratio}`, x: dx + 10, y: dy + 14, fill: '#aaa', fontSize: 9 },
126
+ silent: true,
127
+ });
128
+ }
129
+ }
130
+
131
+ // Vertex labels (X, A, B, C, D)
132
+ for (let i = 0; i < pixelPoints.length && i < LABELS.length; i++) {
133
+ const [px, py] = pixelPoints[i];
134
+ // Place label above or below the point based on neighbors
135
+ const isLocalHigh =
136
+ (i === 0 || py <= pixelPoints[i - 1][1]) &&
137
+ (i === pixelPoints.length - 1 || py <= pixelPoints[i + 1]?.[1]);
138
+ const labelY = isLocalHigh ? py - 14 : py + 16;
139
+
140
+ children.push({
141
+ type: 'text',
142
+ style: {
143
+ text: LABELS[i],
144
+ x: px,
145
+ y: labelY,
146
+ fill: '#e2e8f0',
147
+ fontSize: 12,
148
+ fontWeight: 'bold',
149
+ align: 'center',
150
+ verticalAlign: 'middle',
151
+ },
152
+ silent: true,
153
+ });
154
+ }
155
+
156
+ // Control points
157
+ for (let i = 0; i < pixelPoints.length; i++) {
158
+ const [px, py] = pixelPoints[i];
159
+ children.push({
160
+ type: 'circle',
161
+ name: `point-${i}`,
162
+ shape: { cx: px, cy: py, r: 4 },
163
+ style: {
164
+ fill: '#fff',
165
+ stroke: color,
166
+ lineWidth: 1,
167
+ opacity: isSelected ? 1 : 0,
168
+ },
169
+ z: 100,
170
+ });
171
+ }
172
+
173
+ return {
174
+ type: 'group',
175
+ children,
176
+ };
177
+ }
178
+ }
@@ -0,0 +1,213 @@
1
+ import * as echarts from 'echarts';
2
+ import { AbstractPlugin } from '../../components/AbstractPlugin';
3
+ import { XABCDPatternDrawingRenderer } from './XABCDPatternDrawingRenderer';
4
+
5
+ const LABELS = ['X', 'A', 'B', 'C', 'D'];
6
+ const LEG_COLORS = ['#2196f3', '#ff9800', '#4caf50', '#f44336'];
7
+ const TOTAL_POINTS = 5;
8
+
9
+ export class XABCDPatternTool extends AbstractPlugin {
10
+ private points: number[][] = [];
11
+ private state: 'idle' | 'drawing' | 'finished' = 'idle';
12
+ private graphicGroup: any = null;
13
+
14
+ constructor(options: { name?: string; icon?: string } = {}) {
15
+ super({
16
+ id: 'xabcd-pattern-tool',
17
+ name: options.name || 'XABCD Pattern',
18
+ icon:
19
+ options.icon ||
20
+ `<svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="#e3e3e3" stroke-width="1.5"><polyline points="2,18 6,6 11,14 16,4 21,16"/><circle cx="2" cy="18" r="1.5" fill="#e3e3e3"/><circle cx="6" cy="6" r="1.5" fill="#e3e3e3"/><circle cx="11" cy="14" r="1.5" fill="#e3e3e3"/><circle cx="16" cy="4" r="1.5" fill="#e3e3e3"/><circle cx="21" cy="16" r="1.5" fill="#e3e3e3"/></svg>`,
21
+ });
22
+ }
23
+
24
+ protected onInit(): void {
25
+ this.context.registerDrawingRenderer(new XABCDPatternDrawingRenderer());
26
+ }
27
+
28
+ protected onActivate(): void {
29
+ this.state = 'idle';
30
+ this.points = [];
31
+ this.context.getChart().getZr().setCursorStyle('crosshair');
32
+ this.bindEvents();
33
+ }
34
+
35
+ protected onDeactivate(): void {
36
+ this.state = 'idle';
37
+ this.points = [];
38
+ this.removeGraphic();
39
+ this.unbindEvents();
40
+ this.context.getChart().getZr().setCursorStyle('default');
41
+ }
42
+
43
+ private bindEvents() {
44
+ const zr = this.context.getChart().getZr();
45
+ zr.on('click', this.onClick);
46
+ zr.on('mousemove', this.onMouseMove);
47
+ }
48
+
49
+ private unbindEvents() {
50
+ const zr = this.context.getChart().getZr();
51
+ zr.off('click', this.onClick);
52
+ zr.off('mousemove', this.onMouseMove);
53
+ }
54
+
55
+ private onClick = (params: any) => {
56
+ const pt = this.getPoint(params);
57
+
58
+ if (this.state === 'idle') {
59
+ this.state = 'drawing';
60
+ this.points = [pt, [...pt]]; // First point + cursor preview
61
+ this.initGraphic();
62
+ this.updateGraphic();
63
+ } else if (this.state === 'drawing') {
64
+ // Replace the preview point with the confirmed click
65
+ this.points[this.points.length - 1] = pt;
66
+
67
+ if (this.points.length >= TOTAL_POINTS) {
68
+ // All 5 points placed
69
+ this.state = 'finished';
70
+ this.updateGraphic();
71
+ this.saveDrawing();
72
+ this.removeGraphic();
73
+ this.context.disableTools();
74
+ } else {
75
+ // Add a new preview point for the next position
76
+ this.points.push([...pt]);
77
+ this.updateGraphic();
78
+ }
79
+ }
80
+ };
81
+
82
+ private onMouseMove = (params: any) => {
83
+ if (this.state !== 'drawing' || this.points.length < 2) return;
84
+ // Update the last (preview) point
85
+ this.points[this.points.length - 1] = this.getPoint(params);
86
+ this.updateGraphic();
87
+ };
88
+
89
+ private initGraphic() {
90
+ this.graphicGroup = new echarts.graphic.Group();
91
+ this.context.getChart().getZr().add(this.graphicGroup);
92
+ }
93
+
94
+ private removeGraphic() {
95
+ if (this.graphicGroup) {
96
+ this.context.getChart().getZr().remove(this.graphicGroup);
97
+ this.graphicGroup = null;
98
+ }
99
+ }
100
+
101
+ private updateGraphic() {
102
+ if (!this.graphicGroup) return;
103
+ this.graphicGroup.removeAll();
104
+
105
+ const pts = this.points;
106
+
107
+ // Fill triangles
108
+ if (pts.length >= 3) {
109
+ this.graphicGroup.add(
110
+ new echarts.graphic.Polygon({
111
+ shape: { points: pts.slice(0, 3) },
112
+ style: { fill: 'rgba(33, 150, 243, 0.08)' },
113
+ silent: true,
114
+ }),
115
+ );
116
+ }
117
+ if (pts.length >= 5) {
118
+ this.graphicGroup.add(
119
+ new echarts.graphic.Polygon({
120
+ shape: { points: pts.slice(2, 5) },
121
+ style: { fill: 'rgba(244, 67, 54, 0.08)' },
122
+ silent: true,
123
+ }),
124
+ );
125
+ }
126
+
127
+ // Leg lines
128
+ for (let i = 0; i < pts.length - 1; i++) {
129
+ const [x1, y1] = pts[i];
130
+ const [x2, y2] = pts[i + 1];
131
+ this.graphicGroup.add(
132
+ new echarts.graphic.Line({
133
+ shape: { x1, y1, x2, y2 },
134
+ style: { stroke: LEG_COLORS[i % LEG_COLORS.length], lineWidth: 2 },
135
+ silent: true,
136
+ }),
137
+ );
138
+ }
139
+
140
+ // Dashed connectors X→B, A→C, B→D
141
+ const connectors: [number, number][] = [[0, 2], [1, 3], [2, 4]];
142
+ for (const [from, to] of connectors) {
143
+ if (from < pts.length && to < pts.length) {
144
+ const [x1, y1] = pts[from];
145
+ const [x2, y2] = pts[to];
146
+ this.graphicGroup.add(
147
+ new echarts.graphic.Line({
148
+ shape: { x1, y1, x2, y2 },
149
+ style: { stroke: '#555', lineWidth: 1, lineDash: [4, 4] },
150
+ silent: true,
151
+ }),
152
+ );
153
+ }
154
+ }
155
+
156
+ // Vertex labels
157
+ for (let i = 0; i < pts.length && i < LABELS.length; i++) {
158
+ const [px, py] = pts[i];
159
+ const isLocalHigh =
160
+ (i === 0 || py <= pts[i - 1][1]) &&
161
+ (i === pts.length - 1 || py <= pts[i + 1]?.[1]);
162
+ const labelY = isLocalHigh ? py - 14 : py + 16;
163
+
164
+ this.graphicGroup.add(
165
+ new echarts.graphic.Text({
166
+ style: {
167
+ text: LABELS[i],
168
+ x: px,
169
+ y: labelY,
170
+ fill: '#e2e8f0',
171
+ fontSize: 12,
172
+ fontWeight: 'bold',
173
+ align: 'center',
174
+ verticalAlign: 'middle',
175
+ },
176
+ silent: true,
177
+ }),
178
+ );
179
+ }
180
+
181
+ // Point circles
182
+ for (let i = 0; i < pts.length; i++) {
183
+ const [px, py] = pts[i];
184
+ this.graphicGroup.add(
185
+ new echarts.graphic.Circle({
186
+ shape: { cx: px, cy: py, r: 4 },
187
+ style: { fill: '#fff', stroke: '#3b82f6', lineWidth: 1.5 },
188
+ z: 101,
189
+ silent: true,
190
+ }),
191
+ );
192
+ }
193
+ }
194
+
195
+ private saveDrawing() {
196
+ const dataPoints = this.points.map((pt) =>
197
+ this.context.coordinateConversion.pixelToData({ x: pt[0], y: pt[1] }),
198
+ );
199
+
200
+ if (dataPoints.every((p) => p !== null)) {
201
+ this.context.addDrawing({
202
+ id: `xabcd-${Date.now()}`,
203
+ type: 'xabcd_pattern',
204
+ points: dataPoints as any[],
205
+ paneIndex: dataPoints[0]!.paneIndex || 0,
206
+ style: {
207
+ color: '#3b82f6',
208
+ lineWidth: 2,
209
+ },
210
+ });
211
+ }
212
+ }
213
+ }
@@ -0,0 +1,2 @@
1
+ export { XABCDPatternTool } from './XABCDPatternTool';
2
+ export { XABCDPatternDrawingRenderer } from './XABCDPatternDrawingRenderer';
package/src/types.ts CHANGED
@@ -106,6 +106,7 @@ export interface QFChartOptions {
106
106
  };
107
107
  dataZoom?: {
108
108
  visible?: boolean;
109
+ pannable?: boolean; // Keep pan/drag when visible=false (default true)
109
110
  position?: 'top' | 'bottom';
110
111
  height?: number; // height in %, default 6
111
112
  start?: number; // 0-100, default 50
@@ -117,17 +118,17 @@ export interface QFChartOptions {
117
118
  triggerOn?: 'mousemove' | 'click' | 'none'; // When to show tooltip/crosshair, default 'mousemove'
118
119
  };
119
120
  grid?: {
120
- show?: boolean; // Show/hide split lines (default true)
121
- lineColor?: string; // Split line color (default '#334155')
122
- lineOpacity?: number; // Split line opacity (default 0.5 main, 0.3 indicator panes)
123
- borderColor?: string; // Axis line color (default '#334155')
124
- borderShow?: boolean; // Show/hide axis border lines (default true)
121
+ show?: boolean; // Show/hide split lines (default true)
122
+ lineColor?: string; // Split line color (default '#334155')
123
+ lineOpacity?: number; // Split line opacity (default 0.5 main, 0.3 indicator panes)
124
+ borderColor?: string; // Axis line color (default '#334155')
125
+ borderShow?: boolean; // Show/hide axis border lines (default true)
125
126
  };
126
127
  layout?: {
127
- mainPaneHeight?: string; // e.g. "60%"
128
- gap?: number; // Gap between panes in % (default ~5)
129
- left?: string; // Grid left margin (default '10%')
130
- right?: string; // Grid right margin (default '10%')
128
+ mainPaneHeight?: string; // e.g. "60%"
129
+ gap?: number; // Gap between panes in % (default ~5)
130
+ left?: string; // Grid left margin (default '10%')
131
+ right?: string; // Grid right margin (default '10%')
131
132
  };
132
133
  watermark?: boolean; // Default true
133
134
  }
@@ -176,14 +177,20 @@ export interface ChartContext {
176
177
  // Interaction Locking
177
178
  lockChart(): void;
178
179
  unlockChart(): void;
180
+
181
+ // Drawing Renderer Registration
182
+ registerDrawingRenderer(renderer: DrawingRenderer): void;
183
+
184
+ // Snap to nearest candle OHLC value
185
+ snapToCandle(point: Coordinate): Coordinate;
179
186
  }
180
187
 
181
- export type DrawingType = 'line' | 'fibonacci';
188
+ export type DrawingType = string;
182
189
 
183
190
  export interface DrawingElement {
184
191
  id: string;
185
192
  type: DrawingType;
186
- points: DataCoordinate[]; // [start, end]
193
+ points: DataCoordinate[];
187
194
  paneIndex?: number; // Pane where this drawing belongs (default 0)
188
195
  style?: {
189
196
  color?: string;
@@ -191,6 +198,27 @@ export interface DrawingElement {
191
198
  };
192
199
  }
193
200
 
201
+ // Drawing Renderer System
202
+
203
+ export interface DrawingRenderContext {
204
+ drawing: DrawingElement;
205
+ /** Pixel coords for each point, in the same order as drawing.points */
206
+ pixelPoints: [number, number][];
207
+ /** Whether this drawing is currently selected */
208
+ isSelected: boolean;
209
+ /** The ECharts custom series api object */
210
+ api: any;
211
+ /** Grid coordinate system bounds (x, y, width, height in pixels) */
212
+ coordSys: { x: number; y: number; width: number; height: number };
213
+ }
214
+
215
+ export interface DrawingRenderer {
216
+ /** The drawing type this renderer handles */
217
+ type: string;
218
+ /** Return an ECharts custom series renderItem group element */
219
+ render(ctx: DrawingRenderContext): any;
220
+ }
221
+
194
222
  export interface PluginConfig {
195
223
  id: string;
196
224
  name?: string;