@qfo/qfchart 0.8.1 → 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 (34) hide show
  1. package/dist/index.d.ts +206 -1
  2. package/dist/qfchart.min.browser.js +16 -16
  3. package/dist/qfchart.min.es.js +16 -16
  4. package/package.json +1 -1
  5. package/src/QFChart.ts +11 -10
  6. package/src/components/LayoutManager.ts +51 -17
  7. package/src/index.ts +8 -0
  8. package/src/plugins/CrossLineTool/CrossLineDrawingRenderer.ts +49 -0
  9. package/src/plugins/CrossLineTool/CrossLineTool.ts +52 -0
  10. package/src/plugins/CrossLineTool/index.ts +2 -0
  11. package/src/plugins/ExtendedLineTool/ExtendedLineDrawingRenderer.ts +73 -0
  12. package/src/plugins/ExtendedLineTool/ExtendedLineTool.ts +173 -0
  13. package/src/plugins/ExtendedLineTool/index.ts +2 -0
  14. package/src/plugins/HorizontalLineTool/HorizontalLineDrawingRenderer.ts +54 -0
  15. package/src/plugins/HorizontalLineTool/HorizontalLineTool.ts +52 -0
  16. package/src/plugins/HorizontalLineTool/index.ts +2 -0
  17. package/src/plugins/HorizontalRayTool/HorizontalRayDrawingRenderer.ts +34 -0
  18. package/src/plugins/HorizontalRayTool/HorizontalRayTool.ts +52 -0
  19. package/src/plugins/HorizontalRayTool/index.ts +2 -0
  20. package/src/plugins/InfoLineTool/InfoLineDrawingRenderer.ts +72 -0
  21. package/src/plugins/InfoLineTool/InfoLineTool.ts +130 -0
  22. package/src/plugins/InfoLineTool/index.ts +2 -0
  23. package/src/plugins/LineTool/LineDrawingRenderer.ts +2 -2
  24. package/src/plugins/LineTool/LineTool.ts +5 -5
  25. package/src/plugins/RayTool/RayDrawingRenderer.ts +69 -0
  26. package/src/plugins/RayTool/RayTool.ts +162 -0
  27. package/src/plugins/RayTool/index.ts +2 -0
  28. package/src/plugins/TrendAngleTool/TrendAngleDrawingRenderer.ts +87 -0
  29. package/src/plugins/TrendAngleTool/TrendAngleTool.ts +176 -0
  30. package/src/plugins/TrendAngleTool/index.ts +2 -0
  31. package/src/plugins/VerticalLineTool/VerticalLineDrawingRenderer.ts +35 -0
  32. package/src/plugins/VerticalLineTool/VerticalLineTool.ts +52 -0
  33. package/src/plugins/VerticalLineTool/index.ts +2 -0
  34. package/src/types.ts +2 -0
@@ -0,0 +1,176 @@
1
+ import { AbstractPlugin } from '../../components/AbstractPlugin';
2
+ import { TrendAngleDrawingRenderer } from './TrendAngleDrawingRenderer';
3
+ import * as echarts from 'echarts';
4
+
5
+ const COLOR = '#d1d4dc';
6
+
7
+ type PluginState = 'idle' | 'drawing' | 'finished';
8
+
9
+ export class TrendAngleTool extends AbstractPlugin {
10
+ private zr!: any;
11
+ private state: PluginState = 'idle';
12
+ private startPoint: number[] | null = null;
13
+ private endPoint: number[] | null = null;
14
+ private group: any = null;
15
+ private line: any = null;
16
+ private hRefLine: any = null;
17
+ private arc: any = null;
18
+ private angleText: any = null;
19
+ private startCircle: any = null;
20
+ private endCircle: any = null;
21
+
22
+ constructor(options: { name?: string; icon?: string } = {}) {
23
+ super({
24
+ id: 'trend-angle-tool',
25
+ name: options?.name || 'Trend Angle',
26
+ icon: options?.icon || `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="3" y1="20" x2="21" y2="6"/><line x1="3" y1="20" x2="14" y2="20" opacity="0.4"/><path d="M8 20 A5 5 0 0 1 7 16" stroke-width="1.5"/></svg>`,
27
+ });
28
+ }
29
+
30
+ protected onInit(): void {
31
+ this.zr = this.chart.getZr();
32
+ this.context.registerDrawingRenderer(new TrendAngleDrawingRenderer());
33
+ }
34
+
35
+ protected onActivate(): void {
36
+ this.state = 'idle';
37
+ this.chart.getZr().setCursorStyle('crosshair');
38
+ this.zr.on('click', this.onClick);
39
+ this.zr.on('mousemove', this.onMouseMove);
40
+ }
41
+
42
+ protected onDeactivate(): void {
43
+ this.state = 'idle';
44
+ this.chart.getZr().setCursorStyle('default');
45
+ this.zr.off('click', this.onClick);
46
+ this.zr.off('mousemove', this.onMouseMove);
47
+ this.removeGraphic();
48
+ }
49
+
50
+ protected onDestroy(): void {
51
+ this.removeGraphic();
52
+ }
53
+
54
+ private onClick = (params: any) => {
55
+ if (this.state === 'idle') {
56
+ this.state = 'drawing';
57
+ this.startPoint = this.getPoint(params);
58
+ this.endPoint = this.getPoint(params);
59
+ this.initGraphic();
60
+ this.updateGraphic();
61
+ } else if (this.state === 'drawing') {
62
+ this.state = 'finished';
63
+ this.endPoint = this.getPoint(params);
64
+
65
+ if (this.startPoint && this.endPoint) {
66
+ const start = this.context.coordinateConversion.pixelToData({
67
+ x: this.startPoint[0], y: this.startPoint[1],
68
+ });
69
+ const end = this.context.coordinateConversion.pixelToData({
70
+ x: this.endPoint[0], y: this.endPoint[1],
71
+ });
72
+
73
+ if (start && end) {
74
+ this.context.addDrawing({
75
+ id: `trend-angle-${Date.now()}`,
76
+ type: 'trend-angle',
77
+ points: [start, end],
78
+ paneIndex: start.paneIndex || 0,
79
+ style: { color: COLOR, lineWidth: 1 },
80
+ });
81
+ }
82
+ }
83
+
84
+ this.removeGraphic();
85
+ this.context.disableTools();
86
+ }
87
+ };
88
+
89
+ private onMouseMove = (params: any) => {
90
+ if (this.state !== 'drawing') return;
91
+ this.endPoint = this.getPoint(params);
92
+ this.updateGraphic();
93
+ };
94
+
95
+ private initGraphic(): void {
96
+ if (this.group) return;
97
+ this.group = new echarts.graphic.Group();
98
+ this.line = new echarts.graphic.Line({
99
+ shape: { x1: 0, y1: 0, x2: 0, y2: 0 },
100
+ style: { stroke: COLOR, lineWidth: 1 },
101
+ z: 100,
102
+ });
103
+ this.hRefLine = new echarts.graphic.Line({
104
+ shape: { x1: 0, y1: 0, x2: 0, y2: 0 },
105
+ style: { stroke: COLOR, lineWidth: 1, lineDash: [4, 4], opacity: 0.4 },
106
+ z: 99,
107
+ });
108
+ this.arc = new echarts.graphic.Arc({
109
+ shape: { cx: 0, cy: 0, r: 25, startAngle: 0, endAngle: 0 },
110
+ style: { stroke: COLOR, lineWidth: 1, fill: 'none' },
111
+ z: 99,
112
+ });
113
+ this.angleText = new echarts.graphic.Text({
114
+ style: { text: '', fill: COLOR, fontSize: 11, fontFamily: 'sans-serif' },
115
+ z: 101,
116
+ });
117
+ this.startCircle = new echarts.graphic.Circle({
118
+ shape: { cx: 0, cy: 0, r: 4 },
119
+ style: { fill: '#fff', stroke: COLOR, lineWidth: 1 },
120
+ z: 101,
121
+ });
122
+ this.endCircle = new echarts.graphic.Circle({
123
+ shape: { cx: 0, cy: 0, r: 4 },
124
+ style: { fill: '#fff', stroke: COLOR, lineWidth: 1 },
125
+ z: 101,
126
+ });
127
+ this.group.add(this.hRefLine);
128
+ this.group.add(this.arc);
129
+ this.group.add(this.line);
130
+ this.group.add(this.angleText);
131
+ this.group.add(this.startCircle);
132
+ this.group.add(this.endCircle);
133
+ this.zr.add(this.group);
134
+ }
135
+
136
+ private removeGraphic(): void {
137
+ if (this.group) {
138
+ this.zr.remove(this.group);
139
+ this.group = null;
140
+ }
141
+ }
142
+
143
+ private updateGraphic(): void {
144
+ if (!this.startPoint || !this.endPoint || !this.group) return;
145
+ const [x1, y1] = this.startPoint;
146
+ const [x2, y2] = this.endPoint;
147
+
148
+ this.line.setShape({ x1, y1, x2, y2 });
149
+ this.startCircle.setShape({ cx: x1, cy: y1 });
150
+ this.endCircle.setShape({ cx: x2, cy: y2 });
151
+
152
+ const dx = x2 - x1;
153
+ const dy = y2 - y1;
154
+
155
+ // Horizontal reference from p1
156
+ const hLen = Math.max(Math.abs(dx), 40);
157
+ this.hRefLine.setShape({ x1, y1, x2: x1 + hLen, y2: y1 });
158
+
159
+ // Angle (negate dy for natural angle since screen Y is inverted)
160
+ const angleRad = Math.atan2(-dy, dx);
161
+ const angleDeg = angleRad * (180 / Math.PI);
162
+ const arcR = Math.min(25, Math.sqrt(dx * dx + dy * dy) * 0.3);
163
+
164
+ // Arc from 0 (horizontal) to the line angle
165
+ const screenAngle = Math.atan2(dy, dx); // screen-space angle
166
+ const arcStart = Math.min(0, screenAngle);
167
+ const arcEnd = Math.max(0, screenAngle);
168
+ this.arc.setShape({ cx: x1, cy: y1, r: arcR, startAngle: arcStart, endAngle: arcEnd });
169
+
170
+ // Angle label
171
+ this.angleText.setStyle({ text: `${angleDeg.toFixed(1)}\u00B0` });
172
+ this.angleText.x = x1 + arcR + 6;
173
+ this.angleText.y = y1 + (dy < 0 ? -14 : 2);
174
+ this.angleText.markRedraw();
175
+ }
176
+ }
@@ -0,0 +1,2 @@
1
+ export { TrendAngleTool } from './TrendAngleTool';
2
+ export { TrendAngleDrawingRenderer } from './TrendAngleDrawingRenderer';
@@ -0,0 +1,35 @@
1
+ import { DrawingRenderer, DrawingRenderContext } from '../../types';
2
+
3
+ export class VerticalLineDrawingRenderer implements DrawingRenderer {
4
+ type = 'vertical-line';
5
+
6
+ render(ctx: DrawingRenderContext): any {
7
+ const { drawing, pixelPoints, isSelected, coordSys } = ctx;
8
+ const [px, py] = pixelPoints[0];
9
+ const color = drawing.style?.color || '#d1d4dc';
10
+
11
+ const top = coordSys.y;
12
+ const bottom = coordSys.y + coordSys.height;
13
+
14
+ return {
15
+ type: 'group',
16
+ children: [
17
+ {
18
+ type: 'line',
19
+ name: 'line',
20
+ shape: { x1: px, y1: top, x2: px, y2: bottom },
21
+ style: {
22
+ stroke: color,
23
+ lineWidth: drawing.style?.lineWidth || 1,
24
+ },
25
+ },
26
+ {
27
+ type: 'circle',
28
+ name: 'point-0',
29
+ shape: { cx: px, cy: py, r: 4 },
30
+ style: { fill: '#fff', stroke: color, lineWidth: 1, opacity: isSelected ? 1 : 0 },
31
+ },
32
+ ],
33
+ };
34
+ }
35
+ }
@@ -0,0 +1,52 @@
1
+ import { AbstractPlugin } from '../../components/AbstractPlugin';
2
+ import { VerticalLineDrawingRenderer } from './VerticalLineDrawingRenderer';
3
+
4
+ export class VerticalLineTool extends AbstractPlugin {
5
+ private zr!: any;
6
+
7
+ constructor(options: { name?: string; icon?: string } = {}) {
8
+ super({
9
+ id: 'vertical-line-tool',
10
+ name: options?.name || 'Vertical Line',
11
+ icon: options?.icon || `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="2" x2="12" y2="22"/><circle cx="12" cy="12" r="2" fill="currentColor"/></svg>`,
12
+ });
13
+ }
14
+
15
+ protected onInit(): void {
16
+ this.zr = this.chart.getZr();
17
+ this.context.registerDrawingRenderer(new VerticalLineDrawingRenderer());
18
+ }
19
+
20
+ protected onActivate(): void {
21
+ this.chart.getZr().setCursorStyle('crosshair');
22
+ this.zr.on('click', this.onClick);
23
+ }
24
+
25
+ protected onDeactivate(): void {
26
+ this.chart.getZr().setCursorStyle('default');
27
+ this.zr.off('click', this.onClick);
28
+ }
29
+
30
+ protected onDestroy(): void {}
31
+
32
+ private onClick = (params: any) => {
33
+ const point = this.getPoint(params);
34
+ if (!point) return;
35
+
36
+ const data = this.context.coordinateConversion.pixelToData({
37
+ x: point[0], y: point[1],
38
+ });
39
+
40
+ if (data) {
41
+ this.context.addDrawing({
42
+ id: `vline-${Date.now()}`,
43
+ type: 'vertical-line',
44
+ points: [data],
45
+ paneIndex: data.paneIndex || 0,
46
+ style: { color: '#d1d4dc', lineWidth: 1 },
47
+ });
48
+ }
49
+
50
+ this.context.disableTools();
51
+ };
52
+ }
@@ -0,0 +1,2 @@
1
+ export { VerticalLineTool } from './VerticalLineTool';
2
+ export { VerticalLineDrawingRenderer } from './VerticalLineDrawingRenderer';
package/src/types.ts CHANGED
@@ -208,6 +208,8 @@ export interface DrawingRenderContext {
208
208
  isSelected: boolean;
209
209
  /** The ECharts custom series api object */
210
210
  api: any;
211
+ /** Grid coordinate system bounds (x, y, width, height in pixels) */
212
+ coordSys: { x: number; y: number; width: number; height: number };
211
213
  }
212
214
 
213
215
  export interface DrawingRenderer {