@tradingaction/interactive 2.0.13
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/LICENSE +24 -0
- package/README.md +5 -0
- package/lib/Brush.d.ts +35 -0
- package/lib/Brush.js +103 -0
- package/lib/Brush.js.map +1 -0
- package/lib/ClickCallback.d.ts +18 -0
- package/lib/ClickCallback.js +12 -0
- package/lib/ClickCallback.js.map +1 -0
- package/lib/DrawingObjectSelector.d.ts +18 -0
- package/lib/DrawingObjectSelector.js +73 -0
- package/lib/DrawingObjectSelector.js.map +1 -0
- package/lib/EquidistantChannel.d.ts +74 -0
- package/lib/EquidistantChannel.js +149 -0
- package/lib/EquidistantChannel.js.map +1 -0
- package/lib/FibonacciRetracement.d.ts +89 -0
- package/lib/FibonacciRetracement.js +184 -0
- package/lib/FibonacciRetracement.js.map +1 -0
- package/lib/GannFan.d.ts +78 -0
- package/lib/GannFan.js +128 -0
- package/lib/GannFan.js.map +1 -0
- package/lib/InteractiveText.d.ts +65 -0
- package/lib/InteractiveText.js +83 -0
- package/lib/InteractiveText.js.map +1 -0
- package/lib/InteractiveYCoordinate.d.ts +122 -0
- package/lib/InteractiveYCoordinate.js +100 -0
- package/lib/InteractiveYCoordinate.js.map +1 -0
- package/lib/StandardDeviationChannel.d.ts +74 -0
- package/lib/StandardDeviationChannel.js +125 -0
- package/lib/StandardDeviationChannel.js.map +1 -0
- package/lib/TrendLine.d.ts +84 -0
- package/lib/TrendLine.js +128 -0
- package/lib/TrendLine.js.map +1 -0
- package/lib/ZoomButtons.d.ts +30 -0
- package/lib/ZoomButtons.js +78 -0
- package/lib/ZoomButtons.js.map +1 -0
- package/lib/components/ChannelWithArea.d.ts +30 -0
- package/lib/components/ChannelWithArea.js +126 -0
- package/lib/components/ChannelWithArea.js.map +1 -0
- package/lib/components/ClickableCircle.d.ts +26 -0
- package/lib/components/ClickableCircle.js +48 -0
- package/lib/components/ClickableCircle.js.map +1 -0
- package/lib/components/ClickableShape.d.ts +33 -0
- package/lib/components/ClickableShape.js +64 -0
- package/lib/components/ClickableShape.js.map +1 -0
- package/lib/components/GannFan.d.ts +32 -0
- package/lib/components/GannFan.js +134 -0
- package/lib/components/GannFan.js.map +1 -0
- package/lib/components/HoverTextNearMouse.d.ts +35 -0
- package/lib/components/HoverTextNearMouse.js +113 -0
- package/lib/components/HoverTextNearMouse.js.map +1 -0
- package/lib/components/InteractiveStraightLine.d.ts +61 -0
- package/lib/components/InteractiveStraightLine.js +220 -0
- package/lib/components/InteractiveStraightLine.js.map +1 -0
- package/lib/components/InteractiveText.d.ts +38 -0
- package/lib/components/InteractiveText.js +84 -0
- package/lib/components/InteractiveText.js.map +1 -0
- package/lib/components/InteractiveYCoordinate.d.ts +46 -0
- package/lib/components/InteractiveYCoordinate.js +109 -0
- package/lib/components/InteractiveYCoordinate.js.map +1 -0
- package/lib/components/LinearRegressionChannelWithArea.d.ts +31 -0
- package/lib/components/LinearRegressionChannelWithArea.js +104 -0
- package/lib/components/LinearRegressionChannelWithArea.js.map +1 -0
- package/lib/components/MouseLocationIndicator.d.ts +34 -0
- package/lib/components/MouseLocationIndicator.js +79 -0
- package/lib/components/MouseLocationIndicator.js.map +1 -0
- package/lib/components/Text.d.ts +17 -0
- package/lib/components/Text.js +26 -0
- package/lib/components/Text.js.map +1 -0
- package/lib/components/index.d.ts +11 -0
- package/lib/components/index.js +12 -0
- package/lib/components/index.js.map +1 -0
- package/lib/index.d.ts +12 -0
- package/lib/index.js +13 -0
- package/lib/index.js.map +1 -0
- package/lib/utils.d.ts +7 -0
- package/lib/utils.js +84 -0
- package/lib/utils.js.map +1 -0
- package/lib/wrapper/EachEquidistantChannel.d.ts +59 -0
- package/lib/wrapper/EachEquidistantChannel.js +172 -0
- package/lib/wrapper/EachEquidistantChannel.js.map +1 -0
- package/lib/wrapper/EachFibRetracement.d.ts +81 -0
- package/lib/wrapper/EachFibRetracement.js +202 -0
- package/lib/wrapper/EachFibRetracement.js.map +1 -0
- package/lib/wrapper/EachGannFan.d.ts +81 -0
- package/lib/wrapper/EachGannFan.js +161 -0
- package/lib/wrapper/EachGannFan.js.map +1 -0
- package/lib/wrapper/EachInteractiveYCoordinate.d.ts +50 -0
- package/lib/wrapper/EachInteractiveYCoordinate.js +80 -0
- package/lib/wrapper/EachInteractiveYCoordinate.js.map +1 -0
- package/lib/wrapper/EachLinearRegressionChannel.d.ts +75 -0
- package/lib/wrapper/EachLinearRegressionChannel.js +89 -0
- package/lib/wrapper/EachLinearRegressionChannel.js.map +1 -0
- package/lib/wrapper/EachText.d.ts +59 -0
- package/lib/wrapper/EachText.js +83 -0
- package/lib/wrapper/EachText.js.map +1 -0
- package/lib/wrapper/EachTrendLine.d.ts +85 -0
- package/lib/wrapper/EachTrendLine.js +165 -0
- package/lib/wrapper/EachTrendLine.js.map +1 -0
- package/lib/wrapper/index.d.ts +7 -0
- package/lib/wrapper/index.js +8 -0
- package/lib/wrapper/index.js.map +1 -0
- package/package.json +53 -0
- package/src/Brush.tsx +172 -0
- package/src/ClickCallback.tsx +37 -0
- package/src/DrawingObjectSelector.tsx +97 -0
- package/src/EquidistantChannel.tsx +268 -0
- package/src/FibonacciRetracement.tsx +328 -0
- package/src/GannFan.tsx +233 -0
- package/src/InteractiveText.tsx +182 -0
- package/src/InteractiveYCoordinate.tsx +199 -0
- package/src/StandardDeviationChannel.tsx +269 -0
- package/src/TrendLine.tsx +294 -0
- package/src/ZoomButtons.tsx +165 -0
- package/src/components/ChannelWithArea.tsx +199 -0
- package/src/components/ClickableCircle.tsx +91 -0
- package/src/components/ClickableShape.tsx +114 -0
- package/src/components/GannFan.tsx +189 -0
- package/src/components/HoverTextNearMouse.tsx +174 -0
- package/src/components/InteractiveStraightLine.tsx +335 -0
- package/src/components/InteractiveText.tsx +146 -0
- package/src/components/InteractiveYCoordinate.tsx +200 -0
- package/src/components/LinearRegressionChannelWithArea.tsx +169 -0
- package/src/components/MouseLocationIndicator.tsx +128 -0
- package/src/components/Text.tsx +47 -0
- package/src/components/index.ts +11 -0
- package/src/index.ts +12 -0
- package/src/utils.ts +101 -0
- package/src/wrapper/EachEquidistantChannel.tsx +302 -0
- package/src/wrapper/EachFibRetracement.tsx +359 -0
- package/src/wrapper/EachGannFan.tsx +289 -0
- package/src/wrapper/EachInteractiveYCoordinate.tsx +198 -0
- package/src/wrapper/EachLinearRegressionChannel.tsx +202 -0
- package/src/wrapper/EachText.tsx +190 -0
- package/src/wrapper/EachTrendLine.tsx +331 -0
- package/src/wrapper/index.ts +7 -0
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { head, last, noop } from "@tradingaction/core";
|
|
3
|
+
import { getXValue } from "@tradingaction/core/lib/utils/ChartDataUtil";
|
|
4
|
+
import { isHover, saveNodeType } from "../utils";
|
|
5
|
+
import { ClickableCircle, HoverTextNearMouse, InteractiveStraightLine, generateLine, Text } from "../components";
|
|
6
|
+
import { getNewXY } from "./EachTrendLine";
|
|
7
|
+
|
|
8
|
+
export interface EachFibRetracementProps {
|
|
9
|
+
readonly x1: any;
|
|
10
|
+
readonly x2: any;
|
|
11
|
+
readonly y1: number;
|
|
12
|
+
readonly y2: number;
|
|
13
|
+
readonly yDisplayFormat: (value: number) => string;
|
|
14
|
+
readonly type: string;
|
|
15
|
+
readonly selected: boolean;
|
|
16
|
+
readonly appearance: {
|
|
17
|
+
readonly strokeStyle: string;
|
|
18
|
+
readonly strokeWidth: number;
|
|
19
|
+
readonly fontFamily: string;
|
|
20
|
+
readonly fontSize: number;
|
|
21
|
+
readonly fontFill: string;
|
|
22
|
+
readonly edgeStroke: string;
|
|
23
|
+
readonly edgeFill: string;
|
|
24
|
+
readonly nsEdgeFill: string;
|
|
25
|
+
readonly edgeStrokeWidth: number;
|
|
26
|
+
readonly r: number;
|
|
27
|
+
};
|
|
28
|
+
readonly interactive: boolean;
|
|
29
|
+
readonly hoverText: {
|
|
30
|
+
readonly enable: boolean;
|
|
31
|
+
readonly fontFamily: string;
|
|
32
|
+
readonly fontSize: number;
|
|
33
|
+
readonly fill: string;
|
|
34
|
+
readonly text: string;
|
|
35
|
+
readonly bgFill: string;
|
|
36
|
+
readonly bgOpacity: number;
|
|
37
|
+
readonly bgWidth: number | string;
|
|
38
|
+
readonly bgHeight: number | string;
|
|
39
|
+
readonly selectedText: string;
|
|
40
|
+
};
|
|
41
|
+
readonly index?: number;
|
|
42
|
+
readonly onDrag: (e: React.MouseEvent, index: number | undefined, moreProps: any) => void;
|
|
43
|
+
readonly onDragComplete?: (e: React.MouseEvent, moreProps: any) => void;
|
|
44
|
+
readonly onDoubleClick?: (e: React.MouseEvent, moreProps: any) => void;
|
|
45
|
+
readonly onClickWhenHover?: (e: React.MouseEvent, moreProps: any) => void;
|
|
46
|
+
readonly onClickOutside?: (e: React.MouseEvent, moreProps: any) => void;
|
|
47
|
+
readonly onContextMenuWhenHover?: (e: React.MouseEvent, moreProps: any) => void;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface EachFibRetracementState {
|
|
51
|
+
hover: boolean;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export class EachFibRetracement extends React.Component<EachFibRetracementProps, EachFibRetracementState> {
|
|
55
|
+
public static defaultProps = {
|
|
56
|
+
yDisplayFormat: (d: number) => d.toFixed(2),
|
|
57
|
+
interactive: true,
|
|
58
|
+
appearance: {
|
|
59
|
+
strokeStyle: "#000000",
|
|
60
|
+
strokeWidth: 1,
|
|
61
|
+
fontFamily: "-apple-system, system-ui, Roboto, 'Helvetica Neue', Ubuntu, sans-serif",
|
|
62
|
+
fontSize: 10,
|
|
63
|
+
fontFill: "#000000",
|
|
64
|
+
edgeStroke: "#000000",
|
|
65
|
+
edgeFill: "#FFFFFF",
|
|
66
|
+
nsEdgeFill: "#000000",
|
|
67
|
+
edgeStrokeWidth: 1,
|
|
68
|
+
r: 5,
|
|
69
|
+
},
|
|
70
|
+
selected: false,
|
|
71
|
+
onDrag: noop,
|
|
72
|
+
hoverText: {
|
|
73
|
+
enable: false,
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
private dragStart: any;
|
|
78
|
+
// @ts-ignore
|
|
79
|
+
private isHover: any;
|
|
80
|
+
private saveNodeType: any;
|
|
81
|
+
|
|
82
|
+
public constructor(props: EachFibRetracementProps) {
|
|
83
|
+
super(props);
|
|
84
|
+
|
|
85
|
+
this.isHover = isHover.bind(this);
|
|
86
|
+
this.saveNodeType = saveNodeType.bind(this);
|
|
87
|
+
|
|
88
|
+
this.state = {
|
|
89
|
+
hover: false,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
public render() {
|
|
94
|
+
const { x1, x2, y1, y2 } = this.props;
|
|
95
|
+
const { interactive, yDisplayFormat, type, appearance } = this.props;
|
|
96
|
+
const { strokeStyle, strokeWidth } = appearance;
|
|
97
|
+
const { fontFamily, fontSize, fontFill } = appearance;
|
|
98
|
+
const { edgeStroke, edgeFill, nsEdgeFill, edgeStrokeWidth, r } = appearance;
|
|
99
|
+
const { hoverText, selected } = this.props;
|
|
100
|
+
const { hover } = this.state;
|
|
101
|
+
const { onDragComplete } = this.props;
|
|
102
|
+
const lines = helper({ x1, x2, y1, y2 });
|
|
103
|
+
|
|
104
|
+
const {
|
|
105
|
+
enable: hoverTextEnabled,
|
|
106
|
+
selectedText: hoverTextSelected,
|
|
107
|
+
text: hoverTextUnselected,
|
|
108
|
+
...restHoverTextProps
|
|
109
|
+
} = hoverText;
|
|
110
|
+
|
|
111
|
+
const lineType = type === "EXTEND" ? "XLINE" : type === "BOUND" ? "LINE" : "RAY";
|
|
112
|
+
const dir = head(lines).y1 > last(lines).y1 ? 3 : -1.3;
|
|
113
|
+
|
|
114
|
+
return (
|
|
115
|
+
<g>
|
|
116
|
+
{lines.map((line, j) => {
|
|
117
|
+
const text = `${yDisplayFormat(line.y)} (${line.percent.toFixed(2)}%)`;
|
|
118
|
+
|
|
119
|
+
const xyProvider = ({ xScale, chartConfig }: any) => {
|
|
120
|
+
const { yScale } = chartConfig;
|
|
121
|
+
const {
|
|
122
|
+
x1: lineX1,
|
|
123
|
+
y1: lineY1,
|
|
124
|
+
x2: lineX2,
|
|
125
|
+
} = generateLine({
|
|
126
|
+
type: lineType,
|
|
127
|
+
start: [line.x1, line.y],
|
|
128
|
+
end: [line.x2, line.y],
|
|
129
|
+
xScale,
|
|
130
|
+
yScale,
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
const x = xScale(Math.min(lineX1, lineX2)) + 10;
|
|
134
|
+
const y = yScale(lineY1) + dir * 4;
|
|
135
|
+
return [x, y];
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const firstOrLast = j === 0 || j === lines.length - 1;
|
|
139
|
+
|
|
140
|
+
const interactiveCursorClass = firstOrLast
|
|
141
|
+
? "react-financial-charts-ns-resize-cursor"
|
|
142
|
+
: "react-financial-charts-move-cursor";
|
|
143
|
+
|
|
144
|
+
const interactiveEdgeCursorClass = firstOrLast
|
|
145
|
+
? "react-financial-charts-ns-resize-cursor"
|
|
146
|
+
: "react-financial-charts-ew-resize-cursor";
|
|
147
|
+
|
|
148
|
+
const dragHandler =
|
|
149
|
+
j === 0
|
|
150
|
+
? this.handleLineNSResizeTop
|
|
151
|
+
: j === lines.length - 1
|
|
152
|
+
? this.handleLineNSResizeBottom
|
|
153
|
+
: this.handleLineMove;
|
|
154
|
+
|
|
155
|
+
const edge1DragHandler =
|
|
156
|
+
j === 0
|
|
157
|
+
? this.handleLineNSResizeTop
|
|
158
|
+
: j === lines.length - 1
|
|
159
|
+
? this.handleLineNSResizeBottom
|
|
160
|
+
: this.handleEdge1Drag;
|
|
161
|
+
const edge2DragHandler =
|
|
162
|
+
j === 0
|
|
163
|
+
? this.handleLineNSResizeTop
|
|
164
|
+
: j === lines.length - 1
|
|
165
|
+
? this.handleLineNSResizeBottom
|
|
166
|
+
: this.handleEdge2Drag;
|
|
167
|
+
|
|
168
|
+
const hoverHandler = interactive ? { onHover: this.handleHover, onUnHover: this.handleHover } : {};
|
|
169
|
+
return (
|
|
170
|
+
<g key={j}>
|
|
171
|
+
<InteractiveStraightLine
|
|
172
|
+
onContextMenuWhenHover={(e, moreProps) => {
|
|
173
|
+
if (this.props.onContextMenuWhenHover && j == 0) {
|
|
174
|
+
console.log(this.props);
|
|
175
|
+
this.props.onContextMenuWhenHover(e, moreProps);
|
|
176
|
+
}
|
|
177
|
+
}}
|
|
178
|
+
onDoubleClickWhenHover={this.props.onDoubleClick}
|
|
179
|
+
onClickOutside={this.props.onClickOutside}
|
|
180
|
+
onClickWhenHover={this.props.onClickWhenHover}
|
|
181
|
+
ref={this.saveNodeType(`line_${j}`)}
|
|
182
|
+
selected={selected || hover}
|
|
183
|
+
{...hoverHandler}
|
|
184
|
+
type={lineType}
|
|
185
|
+
x1Value={line.x1}
|
|
186
|
+
y1Value={line.y}
|
|
187
|
+
x2Value={line.x2}
|
|
188
|
+
y2Value={line.y}
|
|
189
|
+
strokeStyle={strokeStyle}
|
|
190
|
+
strokeWidth={hover || selected ? strokeWidth + 1 : strokeWidth}
|
|
191
|
+
interactiveCursorClass={interactiveCursorClass}
|
|
192
|
+
onDragStart={this.handleLineDragStart}
|
|
193
|
+
onDrag={dragHandler}
|
|
194
|
+
onDragComplete={onDragComplete}
|
|
195
|
+
/>
|
|
196
|
+
<Text
|
|
197
|
+
selected={selected}
|
|
198
|
+
xyProvider={xyProvider}
|
|
199
|
+
fontFamily={fontFamily}
|
|
200
|
+
fontSize={fontSize}
|
|
201
|
+
fillStyle={fontFill}
|
|
202
|
+
>
|
|
203
|
+
{text}
|
|
204
|
+
</Text>
|
|
205
|
+
<ClickableCircle
|
|
206
|
+
ref={this.saveNodeType("edge1")}
|
|
207
|
+
show={selected || hover}
|
|
208
|
+
cx={line.x1}
|
|
209
|
+
cy={line.y}
|
|
210
|
+
r={r}
|
|
211
|
+
fillStyle={firstOrLast ? nsEdgeFill : edgeFill}
|
|
212
|
+
strokeStyle={edgeStroke}
|
|
213
|
+
strokeWidth={edgeStrokeWidth}
|
|
214
|
+
interactiveCursorClass={interactiveEdgeCursorClass}
|
|
215
|
+
onDrag={edge1DragHandler}
|
|
216
|
+
onDragComplete={onDragComplete}
|
|
217
|
+
/>
|
|
218
|
+
<ClickableCircle
|
|
219
|
+
ref={this.saveNodeType("edge2")}
|
|
220
|
+
show={selected || hover}
|
|
221
|
+
cx={line.x2}
|
|
222
|
+
cy={line.y}
|
|
223
|
+
r={r}
|
|
224
|
+
fillStyle={firstOrLast ? nsEdgeFill : edgeFill}
|
|
225
|
+
strokeStyle={edgeStroke}
|
|
226
|
+
strokeWidth={edgeStrokeWidth}
|
|
227
|
+
interactiveCursorClass={interactiveEdgeCursorClass}
|
|
228
|
+
onDrag={edge2DragHandler}
|
|
229
|
+
onDragComplete={onDragComplete}
|
|
230
|
+
/>
|
|
231
|
+
</g>
|
|
232
|
+
);
|
|
233
|
+
})}
|
|
234
|
+
<HoverTextNearMouse
|
|
235
|
+
show={hoverTextEnabled && hover}
|
|
236
|
+
{...restHoverTextProps}
|
|
237
|
+
text={selected ? hoverTextSelected : hoverTextUnselected}
|
|
238
|
+
/>
|
|
239
|
+
</g>
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
private readonly handleEdge2Drag = (e: React.MouseEvent, moreProps: any) => {
|
|
244
|
+
const { index, onDrag, x1, y1, y2 } = this.props;
|
|
245
|
+
|
|
246
|
+
const [x2] = getNewXY(moreProps);
|
|
247
|
+
|
|
248
|
+
onDrag(e, index, {
|
|
249
|
+
x1,
|
|
250
|
+
y1,
|
|
251
|
+
x2,
|
|
252
|
+
y2,
|
|
253
|
+
});
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
private readonly handleEdge1Drag = (e: React.MouseEvent, moreProps: any) => {
|
|
257
|
+
const { index, onDrag, y1, x2, y2 } = this.props;
|
|
258
|
+
|
|
259
|
+
const [x1] = getNewXY(moreProps);
|
|
260
|
+
|
|
261
|
+
onDrag(e, index, {
|
|
262
|
+
x1,
|
|
263
|
+
y1,
|
|
264
|
+
x2,
|
|
265
|
+
y2,
|
|
266
|
+
});
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
private readonly handleLineNSResizeBottom = (e: React.MouseEvent, moreProps: any) => {
|
|
270
|
+
const { index, onDrag, x1, y1, x2 } = this.props;
|
|
271
|
+
|
|
272
|
+
const [, y2] = getNewXY(moreProps);
|
|
273
|
+
|
|
274
|
+
onDrag(e, index, {
|
|
275
|
+
x1,
|
|
276
|
+
y1,
|
|
277
|
+
x2,
|
|
278
|
+
y2,
|
|
279
|
+
});
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
private readonly handleLineNSResizeTop = (e: React.MouseEvent, moreProps: any) => {
|
|
283
|
+
const { index, onDrag, x1, x2, y2 } = this.props;
|
|
284
|
+
|
|
285
|
+
const [, y1] = getNewXY(moreProps);
|
|
286
|
+
|
|
287
|
+
onDrag(e, index, {
|
|
288
|
+
x1,
|
|
289
|
+
y1,
|
|
290
|
+
x2,
|
|
291
|
+
y2,
|
|
292
|
+
});
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
private readonly handleLineMove = (e: React.MouseEvent, moreProps: any) => {
|
|
296
|
+
const { index, onDrag } = this.props;
|
|
297
|
+
|
|
298
|
+
const { x1: x1Value, y1: y1Value, x2: x2Value, y2: y2Value } = this.dragStart;
|
|
299
|
+
|
|
300
|
+
const {
|
|
301
|
+
xScale,
|
|
302
|
+
chartConfig: { yScale },
|
|
303
|
+
xAccessor,
|
|
304
|
+
fullData,
|
|
305
|
+
} = moreProps;
|
|
306
|
+
const { startPos, mouseXY } = moreProps;
|
|
307
|
+
|
|
308
|
+
const x1 = xScale(x1Value);
|
|
309
|
+
const y1 = yScale(y1Value);
|
|
310
|
+
const x2 = xScale(x2Value);
|
|
311
|
+
const y2 = yScale(y2Value);
|
|
312
|
+
|
|
313
|
+
const dx = startPos[0] - mouseXY[0];
|
|
314
|
+
const dy = startPos[1] - mouseXY[1];
|
|
315
|
+
|
|
316
|
+
const newX1Value = getXValue(xScale, xAccessor, [x1 - dx, y1 - dy], fullData);
|
|
317
|
+
const newY1Value = yScale.invert(y1 - dy);
|
|
318
|
+
const newX2Value = getXValue(xScale, xAccessor, [x2 - dx, y2 - dy], fullData);
|
|
319
|
+
const newY2Value = yScale.invert(y2 - dy);
|
|
320
|
+
|
|
321
|
+
onDrag(e, index, {
|
|
322
|
+
x1: newX1Value,
|
|
323
|
+
y1: newY1Value,
|
|
324
|
+
x2: newX2Value,
|
|
325
|
+
y2: newY2Value,
|
|
326
|
+
});
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
private readonly handleLineDragStart = () => {
|
|
330
|
+
const { x1, y1, x2, y2 } = this.props;
|
|
331
|
+
|
|
332
|
+
this.dragStart = {
|
|
333
|
+
x1,
|
|
334
|
+
y1,
|
|
335
|
+
x2,
|
|
336
|
+
y2,
|
|
337
|
+
};
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
private readonly handleHover = (_: React.MouseEvent, moreProps: any) => {
|
|
341
|
+
if (this.state.hover !== moreProps.hovering) {
|
|
342
|
+
this.setState({
|
|
343
|
+
hover: moreProps.hovering,
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function helper({ x1, y1, x2, y2 }: any) {
|
|
350
|
+
const dy = y2 - y1;
|
|
351
|
+
const retracements = [100, 61.8, 50, 38.2, 23.6, 0].map((each) => ({
|
|
352
|
+
percent: each,
|
|
353
|
+
x1,
|
|
354
|
+
x2,
|
|
355
|
+
y: y2 - (each / 100) * dy,
|
|
356
|
+
}));
|
|
357
|
+
|
|
358
|
+
return retracements;
|
|
359
|
+
}
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { isDefined, noop } from "@tradingaction/core";
|
|
3
|
+
import { getXValue } from "@tradingaction/core/lib/utils/ChartDataUtil";
|
|
4
|
+
import { isHover, saveNodeType } from "../utils";
|
|
5
|
+
import { ClickableCircle, GannFan, HoverTextNearMouse } from "../components";
|
|
6
|
+
|
|
7
|
+
export interface EachGannFanProps {
|
|
8
|
+
readonly startXY: number[];
|
|
9
|
+
readonly endXY: number[];
|
|
10
|
+
readonly dy?: number;
|
|
11
|
+
readonly interactive: boolean;
|
|
12
|
+
readonly selected: boolean;
|
|
13
|
+
readonly appearance: {
|
|
14
|
+
readonly stroke: string;
|
|
15
|
+
readonly strokeWidth: number;
|
|
16
|
+
readonly edgeStroke: string;
|
|
17
|
+
readonly edgeFill: string;
|
|
18
|
+
readonly edgeStrokeWidth: number;
|
|
19
|
+
readonly r: number;
|
|
20
|
+
readonly fill: Array<string>;
|
|
21
|
+
readonly fontFamily: string;
|
|
22
|
+
readonly fontSize: number;
|
|
23
|
+
readonly fontFill: string;
|
|
24
|
+
};
|
|
25
|
+
readonly hoverText: {
|
|
26
|
+
readonly enable: boolean;
|
|
27
|
+
readonly fontFamily: string;
|
|
28
|
+
readonly fontSize: number;
|
|
29
|
+
readonly fill: string;
|
|
30
|
+
readonly text: string;
|
|
31
|
+
readonly bgFill: string;
|
|
32
|
+
readonly bgOpacity: number;
|
|
33
|
+
readonly bgWidth: number | string;
|
|
34
|
+
readonly bgHeight: number | string;
|
|
35
|
+
readonly selectedText: string;
|
|
36
|
+
};
|
|
37
|
+
readonly index?: number;
|
|
38
|
+
readonly onDrag: (e: React.MouseEvent, index: number | undefined, moreProps: any) => void;
|
|
39
|
+
readonly onDragComplete?: (e: React.MouseEvent, moreProps: any) => void;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface EachGannFanState {
|
|
43
|
+
hover: boolean;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export class EachGannFan extends React.Component<EachGannFanProps, EachGannFanState> {
|
|
47
|
+
public static defaultProps = {
|
|
48
|
+
yDisplayFormat: (d: number) => d.toFixed(2),
|
|
49
|
+
interactive: true,
|
|
50
|
+
selected: false,
|
|
51
|
+
appearance: {
|
|
52
|
+
stroke: "#000000",
|
|
53
|
+
strokeWidth: 1,
|
|
54
|
+
edgeStroke: "#000000",
|
|
55
|
+
edgeFill: "#FFFFFF",
|
|
56
|
+
edgeStrokeWidth: 1,
|
|
57
|
+
r: 5,
|
|
58
|
+
fill: [
|
|
59
|
+
"rgba(31, 119, 180, 0.2)",
|
|
60
|
+
"rgba(255, 126, 14, 0.2)",
|
|
61
|
+
"rgba(44, 160, 44, 0.2)",
|
|
62
|
+
"rgba(214, 39, 39, 0.2)",
|
|
63
|
+
"rgba(148, 103, 189, 0.2)",
|
|
64
|
+
"rgba(140, 86, 75, 0.2)",
|
|
65
|
+
"rgba(227, 119, 194, 0.2)",
|
|
66
|
+
"rgba(127, 127, 127, 0.2)",
|
|
67
|
+
],
|
|
68
|
+
fontFamily: "-apple-system, system-ui, Roboto, 'Helvetica Neue', Ubuntu, sans-serif",
|
|
69
|
+
fontSize: 10,
|
|
70
|
+
fontFill: "#000000",
|
|
71
|
+
},
|
|
72
|
+
onDrag: noop,
|
|
73
|
+
hoverText: {
|
|
74
|
+
...HoverTextNearMouse.defaultProps,
|
|
75
|
+
enable: true,
|
|
76
|
+
bgHeight: 18,
|
|
77
|
+
bgWidth: 120,
|
|
78
|
+
text: "Click to select object",
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// @ts-ignore
|
|
83
|
+
private isHover: any;
|
|
84
|
+
private dragStart: any;
|
|
85
|
+
private saveNodeType: any;
|
|
86
|
+
|
|
87
|
+
public constructor(props: EachGannFanProps) {
|
|
88
|
+
super(props);
|
|
89
|
+
|
|
90
|
+
this.isHover = isHover.bind(this);
|
|
91
|
+
this.saveNodeType = saveNodeType.bind(this);
|
|
92
|
+
|
|
93
|
+
this.state = {
|
|
94
|
+
hover: false,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public render() {
|
|
99
|
+
const { startXY, endXY } = this.props;
|
|
100
|
+
const { interactive, appearance } = this.props;
|
|
101
|
+
const { edgeFill, stroke, strokeWidth, fill } = appearance;
|
|
102
|
+
const { fontFamily, fontSize, fontFill } = appearance;
|
|
103
|
+
const { hoverText, selected } = this.props;
|
|
104
|
+
const { onDragComplete } = this.props;
|
|
105
|
+
const { hover } = this.state;
|
|
106
|
+
const { enable: hoverTextEnabled, ...restHoverTextProps } = hoverText;
|
|
107
|
+
|
|
108
|
+
const hoverHandler = interactive ? { onHover: this.handleHover, onUnHover: this.handleHover } : {};
|
|
109
|
+
|
|
110
|
+
const line1Edge =
|
|
111
|
+
isDefined(startXY) && isDefined(endXY) ? (
|
|
112
|
+
<g>
|
|
113
|
+
{this.getEdgeCircle({
|
|
114
|
+
xy: startXY,
|
|
115
|
+
dragHandler: this.handleLine1Edge1Drag,
|
|
116
|
+
cursor: "react-financial-charts-move-cursor",
|
|
117
|
+
fill: edgeFill,
|
|
118
|
+
edge: "edge1",
|
|
119
|
+
})}
|
|
120
|
+
{this.getEdgeCircle({
|
|
121
|
+
xy: endXY,
|
|
122
|
+
dragHandler: this.handleLine1Edge2Drag,
|
|
123
|
+
cursor: "react-financial-charts-move-cursor",
|
|
124
|
+
fill: edgeFill,
|
|
125
|
+
edge: "edge2",
|
|
126
|
+
})}
|
|
127
|
+
</g>
|
|
128
|
+
) : null;
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<g>
|
|
132
|
+
<GannFan
|
|
133
|
+
ref={this.saveNodeType("fan")}
|
|
134
|
+
selected={hover || selected}
|
|
135
|
+
{...hoverHandler}
|
|
136
|
+
startXY={startXY}
|
|
137
|
+
endXY={endXY}
|
|
138
|
+
strokeStyle={stroke}
|
|
139
|
+
strokeWidth={hover || selected ? strokeWidth + 1 : strokeWidth}
|
|
140
|
+
fillStyle={fill}
|
|
141
|
+
fontFamily={fontFamily}
|
|
142
|
+
fontSize={fontSize}
|
|
143
|
+
fontFill={fontFill}
|
|
144
|
+
interactiveCursorClass="react-financial-charts-move-cursor"
|
|
145
|
+
onDragStart={this.handleDragStart}
|
|
146
|
+
onDrag={this.handleFanDrag}
|
|
147
|
+
onDragComplete={onDragComplete}
|
|
148
|
+
/>
|
|
149
|
+
{line1Edge}
|
|
150
|
+
<HoverTextNearMouse show={hoverTextEnabled && hover && !selected} {...restHoverTextProps} />
|
|
151
|
+
</g>
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
private readonly getEdgeCircle = ({ xy, dragHandler, cursor, fill, edge }: any) => {
|
|
156
|
+
const { hover } = this.state;
|
|
157
|
+
const { selected, appearance } = this.props;
|
|
158
|
+
const { edgeStroke, edgeStrokeWidth, r } = appearance;
|
|
159
|
+
const { onDragComplete } = this.props;
|
|
160
|
+
|
|
161
|
+
return (
|
|
162
|
+
<ClickableCircle
|
|
163
|
+
ref={this.saveNodeType(edge)}
|
|
164
|
+
show={selected || hover}
|
|
165
|
+
cx={xy[0]}
|
|
166
|
+
cy={xy[1]}
|
|
167
|
+
r={r}
|
|
168
|
+
fillStyle={fill}
|
|
169
|
+
strokeStyle={edgeStroke}
|
|
170
|
+
strokeWidth={edgeStrokeWidth}
|
|
171
|
+
interactiveCursorClass={cursor}
|
|
172
|
+
onDragStart={this.handleDragStart}
|
|
173
|
+
onDrag={dragHandler}
|
|
174
|
+
onDragComplete={onDragComplete}
|
|
175
|
+
/>
|
|
176
|
+
);
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
private readonly handleLine1Edge2Drag = (e: React.MouseEvent, moreProps: any) => {
|
|
180
|
+
const { index, onDrag } = this.props;
|
|
181
|
+
const { endXY } = this.dragStart;
|
|
182
|
+
|
|
183
|
+
const {
|
|
184
|
+
startPos,
|
|
185
|
+
mouseXY,
|
|
186
|
+
xAccessor,
|
|
187
|
+
xScale,
|
|
188
|
+
fullData,
|
|
189
|
+
chartConfig: { yScale },
|
|
190
|
+
} = moreProps;
|
|
191
|
+
|
|
192
|
+
const dx = startPos[0] - mouseXY[0];
|
|
193
|
+
const dy = startPos[1] - mouseXY[1];
|
|
194
|
+
|
|
195
|
+
const x1 = xScale(endXY[0]);
|
|
196
|
+
const y1 = yScale(endXY[1]);
|
|
197
|
+
|
|
198
|
+
const newX1Value = getXValue(xScale, xAccessor, [x1 - dx, y1 - dy], fullData);
|
|
199
|
+
const newY1Value = yScale.invert(y1 - dy);
|
|
200
|
+
|
|
201
|
+
onDrag(e, index, {
|
|
202
|
+
startXY: this.dragStart.startXY,
|
|
203
|
+
endXY: [newX1Value, newY1Value],
|
|
204
|
+
dy: this.dragStart.dy,
|
|
205
|
+
});
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
private readonly handleLine1Edge1Drag = (e: React.MouseEvent, moreProps: any) => {
|
|
209
|
+
const { index, onDrag } = this.props;
|
|
210
|
+
const { startXY } = this.dragStart;
|
|
211
|
+
|
|
212
|
+
const {
|
|
213
|
+
startPos,
|
|
214
|
+
mouseXY,
|
|
215
|
+
xAccessor,
|
|
216
|
+
xScale,
|
|
217
|
+
fullData,
|
|
218
|
+
chartConfig: { yScale },
|
|
219
|
+
} = moreProps;
|
|
220
|
+
|
|
221
|
+
const dx = startPos[0] - mouseXY[0];
|
|
222
|
+
const dy = startPos[1] - mouseXY[1];
|
|
223
|
+
|
|
224
|
+
const x1 = xScale(startXY[0]);
|
|
225
|
+
const y1 = yScale(startXY[1]);
|
|
226
|
+
|
|
227
|
+
const newX1Value = getXValue(xScale, xAccessor, [x1 - dx, y1 - dy], fullData);
|
|
228
|
+
const newY1Value = yScale.invert(y1 - dy);
|
|
229
|
+
|
|
230
|
+
onDrag(e, index, {
|
|
231
|
+
startXY: [newX1Value, newY1Value],
|
|
232
|
+
endXY: this.dragStart.endXY,
|
|
233
|
+
dy: this.dragStart.dy,
|
|
234
|
+
});
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
private readonly handleFanDrag = (e: React.MouseEvent, moreProps: any) => {
|
|
238
|
+
const { index, onDrag } = this.props;
|
|
239
|
+
|
|
240
|
+
const { startXY, endXY } = this.dragStart;
|
|
241
|
+
|
|
242
|
+
const {
|
|
243
|
+
xScale,
|
|
244
|
+
chartConfig: { yScale },
|
|
245
|
+
xAccessor,
|
|
246
|
+
fullData,
|
|
247
|
+
} = moreProps;
|
|
248
|
+
const { startPos, mouseXY } = moreProps;
|
|
249
|
+
|
|
250
|
+
const x1 = xScale(startXY[0]);
|
|
251
|
+
const y1 = yScale(startXY[1]);
|
|
252
|
+
const x2 = xScale(endXY[0]);
|
|
253
|
+
const y2 = yScale(endXY[1]);
|
|
254
|
+
|
|
255
|
+
const dx = startPos[0] - mouseXY[0];
|
|
256
|
+
const dy = startPos[1] - mouseXY[1];
|
|
257
|
+
|
|
258
|
+
const newX1Value = getXValue(xScale, xAccessor, [x1 - dx, y1 - dy], fullData);
|
|
259
|
+
const newY1Value = yScale.invert(y1 - dy);
|
|
260
|
+
const newX2Value = getXValue(xScale, xAccessor, [x2 - dx, y2 - dy], fullData);
|
|
261
|
+
const newY2Value = yScale.invert(y2 - dy);
|
|
262
|
+
|
|
263
|
+
// const newDy = newY2Value - endXY[1] + this.dragStart.dy;
|
|
264
|
+
|
|
265
|
+
onDrag(e, index, {
|
|
266
|
+
startXY: [newX1Value, newY1Value],
|
|
267
|
+
endXY: [newX2Value, newY2Value],
|
|
268
|
+
dy: this.dragStart.dy,
|
|
269
|
+
});
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
private readonly handleDragStart = () => {
|
|
273
|
+
const { startXY, endXY, dy } = this.props;
|
|
274
|
+
|
|
275
|
+
this.dragStart = {
|
|
276
|
+
startXY,
|
|
277
|
+
endXY,
|
|
278
|
+
dy,
|
|
279
|
+
};
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
private readonly handleHover = (moreProps: any) => {
|
|
283
|
+
if (this.state.hover !== moreProps.hovering) {
|
|
284
|
+
this.setState({
|
|
285
|
+
hover: moreProps.hovering,
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
}
|