@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,52 @@
1
+ import { AbstractPlugin } from '../../components/AbstractPlugin';
2
+ import { HorizontalLineDrawingRenderer } from './HorizontalLineDrawingRenderer';
3
+
4
+ export class HorizontalLineTool extends AbstractPlugin {
5
+ private zr!: any;
6
+
7
+ constructor(options: { name?: string; icon?: string } = {}) {
8
+ super({
9
+ id: 'horizontal-line-tool',
10
+ name: options?.name || 'Horizontal Line',
11
+ icon: options?.icon || `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="2" y1="12" x2="22" y2="12"/><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 HorizontalLineDrawingRenderer());
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: `hline-${Date.now()}`,
43
+ type: 'horizontal-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 { HorizontalLineTool } from './HorizontalLineTool';
2
+ export { HorizontalLineDrawingRenderer } from './HorizontalLineDrawingRenderer';
@@ -0,0 +1,34 @@
1
+ import { DrawingRenderer, DrawingRenderContext } from '../../types';
2
+
3
+ export class HorizontalRayDrawingRenderer implements DrawingRenderer {
4
+ type = 'horizontal-ray';
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 right = coordSys.x + coordSys.width;
12
+
13
+ return {
14
+ type: 'group',
15
+ children: [
16
+ {
17
+ type: 'line',
18
+ name: 'line',
19
+ shape: { x1: px, y1: py, x2: right, y2: py },
20
+ style: {
21
+ stroke: color,
22
+ lineWidth: drawing.style?.lineWidth || 1,
23
+ },
24
+ },
25
+ {
26
+ type: 'circle',
27
+ name: 'point-0',
28
+ shape: { cx: px, cy: py, r: 4 },
29
+ style: { fill: '#fff', stroke: color, lineWidth: 1, opacity: isSelected ? 1 : 0 },
30
+ },
31
+ ],
32
+ };
33
+ }
34
+ }
@@ -0,0 +1,52 @@
1
+ import { AbstractPlugin } from '../../components/AbstractPlugin';
2
+ import { HorizontalRayDrawingRenderer } from './HorizontalRayDrawingRenderer';
3
+
4
+ export class HorizontalRayTool extends AbstractPlugin {
5
+ private zr!: any;
6
+
7
+ constructor(options: { name?: string; icon?: string } = {}) {
8
+ super({
9
+ id: 'horizontal-ray-tool',
10
+ name: options?.name || 'Horizontal Ray',
11
+ icon: options?.icon || `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="4" y1="12" x2="22" y2="12"/><circle cx="4" 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 HorizontalRayDrawingRenderer());
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: `hray-${Date.now()}`,
43
+ type: 'horizontal-ray',
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 { HorizontalRayTool } from './HorizontalRayTool';
2
+ export { HorizontalRayDrawingRenderer } from './HorizontalRayDrawingRenderer';
@@ -0,0 +1,72 @@
1
+ import { DrawingRenderer, DrawingRenderContext } from '../../types';
2
+
3
+ export class InfoLineDrawingRenderer implements DrawingRenderer {
4
+ type = 'info-line';
5
+
6
+ render(ctx: DrawingRenderContext): any {
7
+ const { drawing, pixelPoints, isSelected } = ctx;
8
+ const [x1, y1] = pixelPoints[0];
9
+ const [x2, y2] = pixelPoints[1];
10
+ const color = drawing.style?.color || '#d1d4dc';
11
+
12
+ const p0 = drawing.points[0];
13
+ const p1 = drawing.points[1];
14
+
15
+ const priceChange = p1.value - p0.value;
16
+ const pctChange = p0.value !== 0 ? (priceChange / p0.value) * 100 : 0;
17
+ const bars = Math.abs(p1.timeIndex - p0.timeIndex);
18
+
19
+ const sign = priceChange >= 0 ? '+' : '';
20
+ const infoText = `${sign}${priceChange.toFixed(2)} (${sign}${pctChange.toFixed(2)}%) ${bars} bars`;
21
+
22
+ // Position info box at midpoint
23
+ const mx = (x1 + x2) / 2;
24
+ const my = (y1 + y2) / 2;
25
+ const isUp = priceChange >= 0;
26
+ const textColor = isUp ? '#26a69a' : '#ef5350';
27
+
28
+ return {
29
+ type: 'group',
30
+ children: [
31
+ {
32
+ type: 'line',
33
+ name: 'line',
34
+ shape: { x1, y1, x2, y2 },
35
+ style: { stroke: color, lineWidth: drawing.style?.lineWidth || 1 },
36
+ },
37
+ // Info box background
38
+ {
39
+ type: 'rect',
40
+ shape: { x: mx - 2, y: my - 22, width: infoText.length * 6.5 + 12, height: 18, r: 3 },
41
+ style: { fill: '#1e293b', stroke: '#475569', lineWidth: 1, opacity: 0.9 },
42
+ z2: 10,
43
+ },
44
+ // Info text
45
+ {
46
+ type: 'text',
47
+ x: mx + 4,
48
+ y: my - 20,
49
+ style: {
50
+ text: infoText,
51
+ fill: textColor,
52
+ fontSize: 11,
53
+ fontFamily: 'monospace',
54
+ },
55
+ z2: 11,
56
+ },
57
+ {
58
+ type: 'circle',
59
+ name: 'point-0',
60
+ shape: { cx: x1, cy: y1, r: 4 },
61
+ style: { fill: '#fff', stroke: color, lineWidth: 1, opacity: isSelected ? 1 : 0 },
62
+ },
63
+ {
64
+ type: 'circle',
65
+ name: 'point-1',
66
+ shape: { cx: x2, cy: y2, r: 4 },
67
+ style: { fill: '#fff', stroke: color, lineWidth: 1, opacity: isSelected ? 1 : 0 },
68
+ },
69
+ ],
70
+ };
71
+ }
72
+ }
@@ -0,0 +1,130 @@
1
+ import { AbstractPlugin } from '../../components/AbstractPlugin';
2
+ import { InfoLineDrawingRenderer } from './InfoLineDrawingRenderer';
3
+ import * as echarts from 'echarts';
4
+
5
+ type PluginState = 'idle' | 'drawing' | 'finished';
6
+
7
+ export class InfoLineTool extends AbstractPlugin {
8
+ private zr!: any;
9
+ private state: PluginState = 'idle';
10
+ private startPoint: number[] | null = null;
11
+ private endPoint: number[] | null = null;
12
+ private group: any = null;
13
+ private line: any = null;
14
+ private startCircle: any = null;
15
+ private endCircle: any = null;
16
+
17
+ constructor(options: { name?: string; icon?: string } = {}) {
18
+ super({
19
+ id: 'info-line-tool',
20
+ name: options?.name || 'Info Line',
21
+ icon: options?.icon || `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="2" y1="22" x2="22" y2="2"/><rect x="12" y="8" width="8" height="5" rx="1" fill="none" stroke-width="1.5"/></svg>`,
22
+ });
23
+ }
24
+
25
+ protected onInit(): void {
26
+ this.zr = this.chart.getZr();
27
+ this.context.registerDrawingRenderer(new InfoLineDrawingRenderer());
28
+ }
29
+
30
+ protected onActivate(): void {
31
+ this.state = 'idle';
32
+ this.chart.getZr().setCursorStyle('crosshair');
33
+ this.zr.on('click', this.onClick);
34
+ this.zr.on('mousemove', this.onMouseMove);
35
+ }
36
+
37
+ protected onDeactivate(): void {
38
+ this.state = 'idle';
39
+ this.chart.getZr().setCursorStyle('default');
40
+ this.zr.off('click', this.onClick);
41
+ this.zr.off('mousemove', this.onMouseMove);
42
+ this.removeGraphic();
43
+ }
44
+
45
+ protected onDestroy(): void {
46
+ this.removeGraphic();
47
+ }
48
+
49
+ private onClick = (params: any) => {
50
+ if (this.state === 'idle') {
51
+ this.state = 'drawing';
52
+ this.startPoint = this.getPoint(params);
53
+ this.endPoint = this.getPoint(params);
54
+ this.initGraphic();
55
+ this.updateGraphic();
56
+ } else if (this.state === 'drawing') {
57
+ this.state = 'finished';
58
+ this.endPoint = this.getPoint(params);
59
+ this.updateGraphic();
60
+
61
+ if (this.startPoint && this.endPoint) {
62
+ const start = this.context.coordinateConversion.pixelToData({
63
+ x: this.startPoint[0], y: this.startPoint[1],
64
+ });
65
+ const end = this.context.coordinateConversion.pixelToData({
66
+ x: this.endPoint[0], y: this.endPoint[1],
67
+ });
68
+
69
+ if (start && end) {
70
+ this.context.addDrawing({
71
+ id: `info-line-${Date.now()}`,
72
+ type: 'info-line',
73
+ points: [start, end],
74
+ paneIndex: start.paneIndex || 0,
75
+ style: { color: '#d1d4dc', lineWidth: 1 },
76
+ });
77
+ }
78
+ }
79
+
80
+ this.removeGraphic();
81
+ this.context.disableTools();
82
+ }
83
+ };
84
+
85
+ private onMouseMove = (params: any) => {
86
+ if (this.state !== 'drawing') return;
87
+ this.endPoint = this.getPoint(params);
88
+ this.updateGraphic();
89
+ };
90
+
91
+ private initGraphic(): void {
92
+ if (this.group) return;
93
+ this.group = new echarts.graphic.Group();
94
+ this.line = new echarts.graphic.Line({
95
+ shape: { x1: 0, y1: 0, x2: 0, y2: 0 },
96
+ style: { stroke: '#d1d4dc', lineWidth: 1 },
97
+ z: 100,
98
+ });
99
+ this.startCircle = new echarts.graphic.Circle({
100
+ shape: { cx: 0, cy: 0, r: 4 },
101
+ style: { fill: '#fff', stroke: '#d1d4dc', lineWidth: 1 },
102
+ z: 101,
103
+ });
104
+ this.endCircle = new echarts.graphic.Circle({
105
+ shape: { cx: 0, cy: 0, r: 4 },
106
+ style: { fill: '#fff', stroke: '#d1d4dc', lineWidth: 1 },
107
+ z: 101,
108
+ });
109
+ this.group.add(this.line);
110
+ this.group.add(this.startCircle);
111
+ this.group.add(this.endCircle);
112
+ this.zr.add(this.group);
113
+ }
114
+
115
+ private removeGraphic(): void {
116
+ if (this.group) {
117
+ this.zr.remove(this.group);
118
+ this.group = null;
119
+ }
120
+ }
121
+
122
+ private updateGraphic(): void {
123
+ if (!this.startPoint || !this.endPoint || !this.group) return;
124
+ const [x1, y1] = this.startPoint;
125
+ const [x2, y2] = this.endPoint;
126
+ this.line.setShape({ x1, y1, x2, y2 });
127
+ this.startCircle.setShape({ cx: x1, cy: y1 });
128
+ this.endCircle.setShape({ cx: x2, cy: y2 });
129
+ }
130
+ }
@@ -0,0 +1,2 @@
1
+ export { InfoLineTool } from './InfoLineTool';
2
+ export { InfoLineDrawingRenderer } from './InfoLineDrawingRenderer';
@@ -0,0 +1,49 @@
1
+ import { DrawingRenderer, DrawingRenderContext } from '../../types';
2
+
3
+ export class LineDrawingRenderer implements DrawingRenderer {
4
+ type = 'line';
5
+
6
+ render(ctx: DrawingRenderContext): any {
7
+ const { drawing, pixelPoints, isSelected } = ctx;
8
+ const [x1, y1] = pixelPoints[0];
9
+ const [x2, y2] = pixelPoints[1];
10
+ const color = drawing.style?.color || '#d1d4dc';
11
+
12
+ return {
13
+ type: 'group',
14
+ children: [
15
+ {
16
+ type: 'line',
17
+ name: 'line',
18
+ shape: { x1, y1, x2, y2 },
19
+ style: {
20
+ stroke: color,
21
+ lineWidth: drawing.style?.lineWidth || 1,
22
+ },
23
+ },
24
+ {
25
+ type: 'circle',
26
+ name: 'point-0',
27
+ shape: { cx: x1, cy: y1, r: 4 },
28
+ style: {
29
+ fill: '#fff',
30
+ stroke: color,
31
+ lineWidth: 1,
32
+ opacity: isSelected ? 1 : 0,
33
+ },
34
+ },
35
+ {
36
+ type: 'circle',
37
+ name: 'point-1',
38
+ shape: { cx: x2, cy: y2, r: 4 },
39
+ style: {
40
+ fill: '#fff',
41
+ stroke: color,
42
+ lineWidth: 1,
43
+ opacity: isSelected ? 1 : 0,
44
+ },
45
+ },
46
+ ],
47
+ };
48
+ }
49
+ }