@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,97 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { head, isDefined, mapObject, GenericComponent, getMouseCanvas } from "@tradingaction/core";
|
|
3
|
+
import { getMorePropsForChart, getSelected } from "./utils";
|
|
4
|
+
|
|
5
|
+
interface DrawingObjectSelectorProps {
|
|
6
|
+
readonly getInteractiveNodes: () => any[];
|
|
7
|
+
readonly onSelect?: (e: React.MouseEvent, interactives: any[], moreProps: any) => void;
|
|
8
|
+
readonly onDoubleClick?: (e: React.MouseEvent, item: any, moreProps: any) => void;
|
|
9
|
+
readonly drawingObjectMap: any;
|
|
10
|
+
readonly enabled: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class DrawingObjectSelector extends React.Component<DrawingObjectSelectorProps> {
|
|
14
|
+
public static defaultProps = {
|
|
15
|
+
enabled: true,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
public render() {
|
|
19
|
+
return (
|
|
20
|
+
<GenericComponent
|
|
21
|
+
canvasToDraw={getMouseCanvas}
|
|
22
|
+
onMouseDown={this.handleClick}
|
|
23
|
+
onDoubleClick={this.handleDoubleClick}
|
|
24
|
+
drawOn={["mousemove", "pan", "drag"]}
|
|
25
|
+
/>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
private readonly getInteraction = (moreProps: any) => {
|
|
30
|
+
const { getInteractiveNodes, drawingObjectMap } = this.props;
|
|
31
|
+
const interactiveNodes = getInteractiveNodes();
|
|
32
|
+
const interactives = mapObject(interactiveNodes, (each) => {
|
|
33
|
+
const key = drawingObjectMap[each.type];
|
|
34
|
+
|
|
35
|
+
const valueArray = isDefined(key) ? each.node.props[key] : undefined;
|
|
36
|
+
|
|
37
|
+
const valuePresent = isDefined(valueArray) && Array.isArray(valueArray) && valueArray.length > 0;
|
|
38
|
+
if (valuePresent) {
|
|
39
|
+
const morePropsForChart = getMorePropsForChart(moreProps, each.chartId);
|
|
40
|
+
|
|
41
|
+
const objects = each.node.getSelectionState(morePropsForChart);
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
type: each.type,
|
|
45
|
+
chartId: each.chartId,
|
|
46
|
+
objects,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
type: each.type,
|
|
51
|
+
chartId: each.chartId,
|
|
52
|
+
objects: [],
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
return interactives;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
private readonly handleClick = (e: React.MouseEvent, moreProps: any) => {
|
|
60
|
+
e.preventDefault();
|
|
61
|
+
const { onSelect } = this.props;
|
|
62
|
+
const { enabled } = this.props;
|
|
63
|
+
if (!enabled) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const interactives = this.getInteraction(moreProps);
|
|
68
|
+
if (onSelect !== undefined) {
|
|
69
|
+
onSelect(e, interactives, moreProps);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
private readonly handleDoubleClick = (e: React.MouseEvent, moreProps: any) => {
|
|
74
|
+
e.preventDefault();
|
|
75
|
+
const { onDoubleClick } = this.props;
|
|
76
|
+
const { enabled } = this.props;
|
|
77
|
+
if (!enabled) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const interactives = this.getInteraction(moreProps);
|
|
82
|
+
const allSelected = getSelected(interactives);
|
|
83
|
+
|
|
84
|
+
if (allSelected.length > 0) {
|
|
85
|
+
const selected = head(allSelected);
|
|
86
|
+
const item = {
|
|
87
|
+
type: selected.type,
|
|
88
|
+
chartId: selected.chartId,
|
|
89
|
+
object: head(selected.objects),
|
|
90
|
+
};
|
|
91
|
+
const morePropsForChart = getMorePropsForChart(moreProps, selected.chartId);
|
|
92
|
+
if (onDoubleClick !== undefined) {
|
|
93
|
+
onDoubleClick(e, item, morePropsForChart);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { isDefined, isNotDefined, noop } from "@tradingaction/core";
|
|
3
|
+
import { HoverTextNearMouse, MouseLocationIndicator } from "./components";
|
|
4
|
+
import { getSlope, getYIntercept } from "./components/InteractiveStraightLine";
|
|
5
|
+
import { isHoverForInteractiveType, saveNodeType, terminate } from "./utils";
|
|
6
|
+
import { EachEquidistantChannel } from "./wrapper";
|
|
7
|
+
|
|
8
|
+
interface EquidistantChannelProps {
|
|
9
|
+
readonly enabled: boolean;
|
|
10
|
+
readonly onStart: () => void;
|
|
11
|
+
readonly onComplete: (e: React.MouseEvent, newChannels: any[], moreProps: any) => void;
|
|
12
|
+
readonly onSelect: (e: React.MouseEvent, interactives: any[], moreProps: any) => void;
|
|
13
|
+
readonly currentPositionStroke?: string;
|
|
14
|
+
readonly currentPositionStrokeWidth?: number;
|
|
15
|
+
readonly currentPositionOpacity?: number;
|
|
16
|
+
readonly currentPositionRadius?: number;
|
|
17
|
+
readonly hoverText: object;
|
|
18
|
+
readonly channels: any[];
|
|
19
|
+
readonly appearance: {
|
|
20
|
+
readonly stroke: string;
|
|
21
|
+
readonly strokeOpacity: number;
|
|
22
|
+
readonly strokeWidth: number;
|
|
23
|
+
readonly fill: string;
|
|
24
|
+
readonly fillOpacity: number;
|
|
25
|
+
readonly edgeStroke: string;
|
|
26
|
+
readonly edgeFill: string;
|
|
27
|
+
readonly edgeFill2: string;
|
|
28
|
+
readonly edgeStrokeWidth: number;
|
|
29
|
+
readonly r: number;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface EquidistantChannelState {
|
|
34
|
+
current?: any;
|
|
35
|
+
override?: any;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export class EquidistantChannel extends React.Component<EquidistantChannelProps, EquidistantChannelState> {
|
|
39
|
+
public static defaultProps = {
|
|
40
|
+
onSelect: noop,
|
|
41
|
+
currentPositionStroke: "#000000",
|
|
42
|
+
currentPositionOpacity: 1,
|
|
43
|
+
currentPositionStrokeWidth: 3,
|
|
44
|
+
currentPositionRadius: 4,
|
|
45
|
+
hoverText: {
|
|
46
|
+
...HoverTextNearMouse.defaultProps,
|
|
47
|
+
enable: true,
|
|
48
|
+
bgHeight: 18,
|
|
49
|
+
bgWidth: 120,
|
|
50
|
+
text: "Click to select object",
|
|
51
|
+
},
|
|
52
|
+
channels: [],
|
|
53
|
+
appearance: {
|
|
54
|
+
stroke: "#000000",
|
|
55
|
+
strokeOpacity: 1,
|
|
56
|
+
strokeWidth: 1,
|
|
57
|
+
fill: "#8AAFE2",
|
|
58
|
+
fillOpacity: 0.7,
|
|
59
|
+
edgeStroke: "#000000",
|
|
60
|
+
edgeFill: "#FFFFFF",
|
|
61
|
+
edgeFill2: "#250B98",
|
|
62
|
+
edgeStrokeWidth: 1,
|
|
63
|
+
r: 5,
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// @ts-ignore
|
|
68
|
+
private terminate: () => void;
|
|
69
|
+
private saveNodeType: any;
|
|
70
|
+
// @ts-ignore
|
|
71
|
+
private getSelectionState: any;
|
|
72
|
+
private mouseMoved: any;
|
|
73
|
+
|
|
74
|
+
public constructor(props: EquidistantChannelProps) {
|
|
75
|
+
super(props);
|
|
76
|
+
|
|
77
|
+
this.terminate = terminate.bind(this);
|
|
78
|
+
this.saveNodeType = saveNodeType.bind(this);
|
|
79
|
+
this.getSelectionState = isHoverForInteractiveType("channels").bind(this);
|
|
80
|
+
|
|
81
|
+
this.state = {};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
public render() {
|
|
85
|
+
const {
|
|
86
|
+
appearance,
|
|
87
|
+
channels,
|
|
88
|
+
currentPositionOpacity,
|
|
89
|
+
currentPositionRadius = EquidistantChannel.defaultProps.currentPositionRadius,
|
|
90
|
+
currentPositionStroke,
|
|
91
|
+
currentPositionStrokeWidth,
|
|
92
|
+
enabled,
|
|
93
|
+
hoverText,
|
|
94
|
+
} = this.props;
|
|
95
|
+
|
|
96
|
+
const { current, override } = this.state;
|
|
97
|
+
|
|
98
|
+
const overrideIndex = isDefined(override) ? override.index : null;
|
|
99
|
+
|
|
100
|
+
const tempChannel =
|
|
101
|
+
isDefined(current) && isDefined(current.endXY) ? (
|
|
102
|
+
<EachEquidistantChannel
|
|
103
|
+
interactive={false}
|
|
104
|
+
{...current}
|
|
105
|
+
appearance={appearance}
|
|
106
|
+
hoverText={hoverText}
|
|
107
|
+
/>
|
|
108
|
+
) : null;
|
|
109
|
+
|
|
110
|
+
return (
|
|
111
|
+
<g>
|
|
112
|
+
{channels.map((each, idx) => {
|
|
113
|
+
const eachAppearance = isDefined(each.appearance)
|
|
114
|
+
? { ...appearance, ...each.appearance }
|
|
115
|
+
: appearance;
|
|
116
|
+
|
|
117
|
+
return (
|
|
118
|
+
<EachEquidistantChannel
|
|
119
|
+
key={idx}
|
|
120
|
+
ref={this.saveNodeType(idx)}
|
|
121
|
+
index={idx}
|
|
122
|
+
selected={each.selected}
|
|
123
|
+
hoverText={hoverText}
|
|
124
|
+
{...(idx === overrideIndex ? override : each)}
|
|
125
|
+
appearance={eachAppearance}
|
|
126
|
+
onDrag={this.handleDragChannel}
|
|
127
|
+
onDragComplete={this.handleDragChannelComplete}
|
|
128
|
+
/>
|
|
129
|
+
);
|
|
130
|
+
})}
|
|
131
|
+
{tempChannel}
|
|
132
|
+
<MouseLocationIndicator
|
|
133
|
+
enabled={enabled}
|
|
134
|
+
snap={false}
|
|
135
|
+
r={currentPositionRadius}
|
|
136
|
+
stroke={currentPositionStroke}
|
|
137
|
+
opacity={currentPositionOpacity}
|
|
138
|
+
strokeWidth={currentPositionStrokeWidth}
|
|
139
|
+
onMouseDown={this.handleStart}
|
|
140
|
+
onClick={this.handleEnd}
|
|
141
|
+
onMouseMove={this.handleDrawChannel}
|
|
142
|
+
/>
|
|
143
|
+
</g>
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private readonly handleDragChannel = (_: React.MouseEvent, index: any, newXYValue: any) => {
|
|
148
|
+
this.setState({
|
|
149
|
+
override: {
|
|
150
|
+
index,
|
|
151
|
+
...newXYValue,
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
private readonly handleDragChannelComplete = (e: React.MouseEvent, moreProps: any) => {
|
|
157
|
+
const { override } = this.state;
|
|
158
|
+
const { channels } = this.props;
|
|
159
|
+
|
|
160
|
+
if (isDefined(override)) {
|
|
161
|
+
const { index, ...rest } = override;
|
|
162
|
+
const newChannels = channels.map((each, idx) =>
|
|
163
|
+
idx === index ? { ...each, ...rest, selected: true } : each,
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
this.setState(
|
|
167
|
+
{
|
|
168
|
+
override: null,
|
|
169
|
+
},
|
|
170
|
+
() => {
|
|
171
|
+
const { onComplete } = this.props;
|
|
172
|
+
if (onComplete !== undefined) {
|
|
173
|
+
onComplete(e, newChannels, moreProps);
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
private readonly handleStart = (_: React.MouseEvent, xyValue: any) => {
|
|
181
|
+
const { current } = this.state;
|
|
182
|
+
|
|
183
|
+
if (isNotDefined(current) || isNotDefined(current.startXY)) {
|
|
184
|
+
this.mouseMoved = false;
|
|
185
|
+
this.setState(
|
|
186
|
+
{
|
|
187
|
+
current: {
|
|
188
|
+
startXY: xyValue,
|
|
189
|
+
endXY: null,
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
() => {
|
|
193
|
+
const { onStart } = this.props;
|
|
194
|
+
if (onStart !== undefined) {
|
|
195
|
+
onStart();
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
private readonly handleEnd = (e: React.MouseEvent, _: any, moreProps: any) => {
|
|
203
|
+
const { current } = this.state;
|
|
204
|
+
const { channels, appearance } = this.props;
|
|
205
|
+
|
|
206
|
+
if (this.mouseMoved && isDefined(current) && isDefined(current.startXY)) {
|
|
207
|
+
if (isNotDefined(current.dy)) {
|
|
208
|
+
this.setState({
|
|
209
|
+
current: {
|
|
210
|
+
...current,
|
|
211
|
+
dy: 0,
|
|
212
|
+
},
|
|
213
|
+
});
|
|
214
|
+
} else {
|
|
215
|
+
const newChannels = [
|
|
216
|
+
...channels.map((d) => ({ ...d, selected: false })),
|
|
217
|
+
{
|
|
218
|
+
...current,
|
|
219
|
+
selected: true,
|
|
220
|
+
appearance,
|
|
221
|
+
},
|
|
222
|
+
];
|
|
223
|
+
|
|
224
|
+
this.setState(
|
|
225
|
+
{
|
|
226
|
+
current: null,
|
|
227
|
+
},
|
|
228
|
+
() => {
|
|
229
|
+
const { onComplete } = this.props;
|
|
230
|
+
if (onComplete !== undefined) {
|
|
231
|
+
onComplete(e, newChannels, moreProps);
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
private readonly handleDrawChannel = (_: React.MouseEvent, xyValue: any) => {
|
|
240
|
+
const { current } = this.state;
|
|
241
|
+
|
|
242
|
+
if (isDefined(current) && isDefined(current.startXY)) {
|
|
243
|
+
this.mouseMoved = true;
|
|
244
|
+
if (isNotDefined(current.dy)) {
|
|
245
|
+
this.setState({
|
|
246
|
+
current: {
|
|
247
|
+
startXY: current.startXY,
|
|
248
|
+
endXY: xyValue,
|
|
249
|
+
},
|
|
250
|
+
});
|
|
251
|
+
} else {
|
|
252
|
+
const m = getSlope(current.startXY, current.endXY);
|
|
253
|
+
const b = getYIntercept(m, current.endXY);
|
|
254
|
+
|
|
255
|
+
// @ts-ignore
|
|
256
|
+
const y = m * xyValue[0] + b;
|
|
257
|
+
const dy = xyValue[1] - y;
|
|
258
|
+
|
|
259
|
+
this.setState({
|
|
260
|
+
current: {
|
|
261
|
+
...current,
|
|
262
|
+
dy,
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
}
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { isDefined, isNotDefined, noop } from "@tradingaction/core";
|
|
3
|
+
import { HoverTextNearMouse, MouseLocationIndicator } from "./components";
|
|
4
|
+
import { isHoverForInteractiveType, saveNodeType, terminate } from "./utils";
|
|
5
|
+
import { EachFibRetracement } from "./wrapper";
|
|
6
|
+
|
|
7
|
+
interface FibonacciRetracementProps {
|
|
8
|
+
readonly enabled: boolean;
|
|
9
|
+
readonly width?: number;
|
|
10
|
+
readonly onStart?: (moreProps: any) => void;
|
|
11
|
+
readonly onComplete?: (e: React.MouseEvent, newRetracements: any[], moreProps: any) => void;
|
|
12
|
+
readonly onSelect?: (e: React.MouseEvent, interactives: any[], moreProps: any) => void;
|
|
13
|
+
readonly onDoubleClick?: (e: React.MouseEvent, moreProps: any, idx: any) => void;
|
|
14
|
+
readonly onClickWhenHover?: (e: React.MouseEvent, moreProps: any, idx: any) => void;
|
|
15
|
+
readonly onClickOutside?: (e: React.MouseEvent, moreProps: any, idx: any) => void;
|
|
16
|
+
readonly onConextMenu?: (e: React.MouseEvent, moreProps: any, idx: any) => void;
|
|
17
|
+
readonly type:
|
|
18
|
+
| "EXTEND" // extends from -Infinity to +Infinity
|
|
19
|
+
| "RAY" // extends to +/-Infinity in one direction
|
|
20
|
+
| "BOUND"; // extends between the set bounds
|
|
21
|
+
readonly hoverText: object;
|
|
22
|
+
readonly currentPositionStroke?: string;
|
|
23
|
+
readonly currentPositionStrokeWidth?: number;
|
|
24
|
+
readonly currentPositionOpacity?: number;
|
|
25
|
+
readonly currentPositionRadius?: number;
|
|
26
|
+
readonly retracements: any[];
|
|
27
|
+
readonly appearance: {
|
|
28
|
+
readonly strokeStyle: string;
|
|
29
|
+
readonly strokeWidth: number;
|
|
30
|
+
readonly fontFamily: string;
|
|
31
|
+
readonly fontSize: number;
|
|
32
|
+
readonly fontFill: string;
|
|
33
|
+
readonly edgeStroke: string;
|
|
34
|
+
readonly edgeFill: string;
|
|
35
|
+
readonly nsEdgeFill: string;
|
|
36
|
+
readonly edgeStrokeWidth: number;
|
|
37
|
+
readonly r: number;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface FibonacciRetracementState {
|
|
42
|
+
current?: any;
|
|
43
|
+
override?: any;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export class FibonacciRetracement extends React.Component<FibonacciRetracementProps, FibonacciRetracementState> {
|
|
47
|
+
public static defaultProps = {
|
|
48
|
+
enabled: true,
|
|
49
|
+
type: "RAY",
|
|
50
|
+
retracements: [],
|
|
51
|
+
onSelect: noop,
|
|
52
|
+
hoverText: {
|
|
53
|
+
...HoverTextNearMouse.defaultProps,
|
|
54
|
+
enable: true,
|
|
55
|
+
bgHeight: "auto",
|
|
56
|
+
bgWidth: "auto",
|
|
57
|
+
text: "Click to select object",
|
|
58
|
+
selectedText: "",
|
|
59
|
+
},
|
|
60
|
+
currentPositionStroke: "#000000",
|
|
61
|
+
currentPositionOpacity: 1,
|
|
62
|
+
currentPositionStrokeWidth: 3,
|
|
63
|
+
currentPositionRadius: 4,
|
|
64
|
+
appearance: {
|
|
65
|
+
strokeStyle: "#000000",
|
|
66
|
+
strokeWidth: 1,
|
|
67
|
+
fontFamily: "-apple-system, system-ui, Roboto, 'Helvetica Neue', Ubuntu, sans-serif",
|
|
68
|
+
fontSize: 11,
|
|
69
|
+
fontFill: "#000000",
|
|
70
|
+
edgeStroke: "#000000",
|
|
71
|
+
edgeFill: "#FFFFFF",
|
|
72
|
+
nsEdgeFill: "#000000",
|
|
73
|
+
edgeStrokeWidth: 1,
|
|
74
|
+
r: 5,
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// @ts-ignore
|
|
79
|
+
private getSelectionState: any;
|
|
80
|
+
private mouseMoved: any;
|
|
81
|
+
private saveNodeType: any;
|
|
82
|
+
// @ts-ignore
|
|
83
|
+
private terminate: any;
|
|
84
|
+
|
|
85
|
+
public constructor(props: FibonacciRetracementProps) {
|
|
86
|
+
super(props);
|
|
87
|
+
|
|
88
|
+
this.handleEdge1Drag = this.handleEdge1Drag.bind(this);
|
|
89
|
+
this.handleEdge2Drag = this.handleEdge2Drag.bind(this);
|
|
90
|
+
|
|
91
|
+
this.terminate = terminate.bind(this);
|
|
92
|
+
this.getSelectionState = isHoverForInteractiveType("retracements").bind(this);
|
|
93
|
+
|
|
94
|
+
this.saveNodeType = saveNodeType.bind(this);
|
|
95
|
+
this.state = {};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public render() {
|
|
99
|
+
const { current, override } = this.state;
|
|
100
|
+
|
|
101
|
+
const {
|
|
102
|
+
appearance,
|
|
103
|
+
currentPositionStroke,
|
|
104
|
+
currentPositionOpacity,
|
|
105
|
+
currentPositionStrokeWidth,
|
|
106
|
+
currentPositionRadius = FibonacciRetracement.defaultProps.currentPositionRadius,
|
|
107
|
+
retracements,
|
|
108
|
+
type,
|
|
109
|
+
} = this.props;
|
|
110
|
+
|
|
111
|
+
const { enabled, hoverText } = this.props;
|
|
112
|
+
const overrideIndex = isDefined(override) ? override.index : null;
|
|
113
|
+
const hoverTextWidthDefault = {
|
|
114
|
+
...FibonacciRetracement.defaultProps.hoverText,
|
|
115
|
+
...hoverText,
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const currentRetracement =
|
|
119
|
+
isDefined(current) && isDefined(current.x2) ? (
|
|
120
|
+
<EachFibRetracement
|
|
121
|
+
interactive={false}
|
|
122
|
+
type={type}
|
|
123
|
+
appearance={appearance}
|
|
124
|
+
hoverText={hoverTextWidthDefault}
|
|
125
|
+
{...current}
|
|
126
|
+
onDoubleClick={(e) => this.handleDoubleClick(e, {}, 0)}
|
|
127
|
+
/>
|
|
128
|
+
) : null;
|
|
129
|
+
return (
|
|
130
|
+
<g>
|
|
131
|
+
{retracements.map((each, idx) => {
|
|
132
|
+
const eachAppearance = isDefined(each.appearance)
|
|
133
|
+
? { ...appearance, ...each.appearance }
|
|
134
|
+
: appearance;
|
|
135
|
+
|
|
136
|
+
const eachHoverText = isDefined(each.hoverText)
|
|
137
|
+
? { ...hoverTextWidthDefault, ...each.hoverText }
|
|
138
|
+
: hoverTextWidthDefault;
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
<EachFibRetracement
|
|
142
|
+
key={idx}
|
|
143
|
+
ref={this.saveNodeType(idx)}
|
|
144
|
+
index={idx}
|
|
145
|
+
type={each.type}
|
|
146
|
+
selected={each.selected}
|
|
147
|
+
{...(idx === overrideIndex ? override : each)}
|
|
148
|
+
hoverText={eachHoverText}
|
|
149
|
+
appearance={eachAppearance}
|
|
150
|
+
onDrag={this.handleDrag}
|
|
151
|
+
onDragComplete={this.handleDragComplete}
|
|
152
|
+
onDoubleClick={(e, moreProps) => this.handleDoubleClick(e, moreProps, idx)}
|
|
153
|
+
onClickWhenHover={(e, moreProps) => this.handleClickWhenHover(e, moreProps, idx)}
|
|
154
|
+
onClickOutside={(e, moreProps) => this.handleClickOutside(e, moreProps, idx)}
|
|
155
|
+
onContextMenuWhenHover={(e, moreProps) => this.handleConextMenu(e, moreProps, idx)}
|
|
156
|
+
/>
|
|
157
|
+
);
|
|
158
|
+
})}
|
|
159
|
+
{currentRetracement}
|
|
160
|
+
<MouseLocationIndicator
|
|
161
|
+
enabled={enabled}
|
|
162
|
+
snap={false}
|
|
163
|
+
r={currentPositionRadius}
|
|
164
|
+
stroke={currentPositionStroke}
|
|
165
|
+
opacity={currentPositionOpacity}
|
|
166
|
+
strokeWidth={currentPositionStrokeWidth}
|
|
167
|
+
onMouseDown={this.handleStart}
|
|
168
|
+
onClick={this.handleEnd}
|
|
169
|
+
onMouseMove={(e, xyValues) => this.handleDrawRetracement(e, xyValues)}
|
|
170
|
+
/>
|
|
171
|
+
</g>
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
private readonly handleDoubleClick = (_: React.MouseEvent, moreProps: any, idx: any) => {
|
|
176
|
+
if (this.props.onDoubleClick) {
|
|
177
|
+
this.props.onDoubleClick(_, moreProps, idx);
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
private readonly handleClickWhenHover = (_: React.MouseEvent, moreProps: any, idx: any) => {
|
|
181
|
+
if (this.props.onClickWhenHover) {
|
|
182
|
+
this.props.onClickWhenHover(_, moreProps, idx);
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
private readonly handleClickOutside = (_: React.MouseEvent, moreProps: any, idx: any) => {
|
|
186
|
+
if (this.props.onClickOutside) {
|
|
187
|
+
this.props.onClickOutside(_, moreProps, idx);
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
private readonly handleConextMenu = (_: React.MouseEvent, moreProps: any, idx: any) => {
|
|
191
|
+
if (this.props.onConextMenu) {
|
|
192
|
+
_.stopPropagation();
|
|
193
|
+
_.preventDefault();
|
|
194
|
+
this.props.onConextMenu(_, moreProps, idx);
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
private readonly handleDrawRetracement = (_: React.MouseEvent, xyValue: any) => {
|
|
199
|
+
const { current } = this.state;
|
|
200
|
+
if (isDefined(current) && isDefined(current.x1)) {
|
|
201
|
+
this.mouseMoved = true;
|
|
202
|
+
this.setState({
|
|
203
|
+
current: {
|
|
204
|
+
...current,
|
|
205
|
+
x2: xyValue[0],
|
|
206
|
+
y2: xyValue[1],
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
private readonly handleEdge1Drag = (_: React.MouseEvent, echo: any, newXYValue: any, origXYValue: any) => {
|
|
213
|
+
const { retracements } = this.props;
|
|
214
|
+
const { index } = echo;
|
|
215
|
+
|
|
216
|
+
const dx = origXYValue.x1Value - newXYValue.x1Value;
|
|
217
|
+
|
|
218
|
+
this.setState({
|
|
219
|
+
override: {
|
|
220
|
+
index,
|
|
221
|
+
x1: retracements[index].x1 - dx,
|
|
222
|
+
y1: retracements[index].y1,
|
|
223
|
+
x2: retracements[index].x2,
|
|
224
|
+
y2: retracements[index].y2,
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
private readonly handleDrag = (_: React.MouseEvent, index: any, xy: any) => {
|
|
230
|
+
this.setState({
|
|
231
|
+
override: {
|
|
232
|
+
index,
|
|
233
|
+
...xy,
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
private readonly handleEdge2Drag = (_: React.MouseEvent, echo: any, newXYValue: any, origXYValue: any) => {
|
|
239
|
+
const { retracements } = this.props;
|
|
240
|
+
const { index } = echo;
|
|
241
|
+
|
|
242
|
+
const dx = origXYValue.x2Value - newXYValue.x2Value;
|
|
243
|
+
|
|
244
|
+
this.setState({
|
|
245
|
+
override: {
|
|
246
|
+
index,
|
|
247
|
+
x1: retracements[index].x1,
|
|
248
|
+
y1: retracements[index].y1,
|
|
249
|
+
x2: retracements[index].x2 - dx,
|
|
250
|
+
y2: retracements[index].y2,
|
|
251
|
+
},
|
|
252
|
+
});
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
private readonly handleDragComplete = (e: React.MouseEvent, moreProps: any) => {
|
|
256
|
+
const { retracements } = this.props;
|
|
257
|
+
const { override } = this.state;
|
|
258
|
+
if (isDefined(override)) {
|
|
259
|
+
const { index, ...rest } = override;
|
|
260
|
+
|
|
261
|
+
const newRetracements = retracements.map((each, idx) =>
|
|
262
|
+
idx === index ? { ...each, ...rest, selected: true } : each,
|
|
263
|
+
);
|
|
264
|
+
this.setState(
|
|
265
|
+
{
|
|
266
|
+
override: null,
|
|
267
|
+
},
|
|
268
|
+
() => {
|
|
269
|
+
const { onComplete } = this.props;
|
|
270
|
+
if (onComplete !== undefined) {
|
|
271
|
+
onComplete(e, newRetracements, moreProps);
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
private readonly handleStart = (e: React.MouseEvent, xyValue: any, moreProps: any) => {
|
|
279
|
+
const { current } = this.state;
|
|
280
|
+
if (isNotDefined(current) || isNotDefined(current.x1)) {
|
|
281
|
+
this.mouseMoved = false;
|
|
282
|
+
this.setState(
|
|
283
|
+
{
|
|
284
|
+
current: {
|
|
285
|
+
x1: xyValue[0],
|
|
286
|
+
y1: xyValue[1],
|
|
287
|
+
x2: null,
|
|
288
|
+
y2: null,
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
() => {
|
|
292
|
+
const { onStart } = this.props;
|
|
293
|
+
if (onStart !== undefined) {
|
|
294
|
+
onStart(moreProps);
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
private readonly handleEnd = (e: React.MouseEvent, xyValue: any, moreProps: any) => {
|
|
302
|
+
const { retracements, appearance, type } = this.props;
|
|
303
|
+
const { current } = this.state;
|
|
304
|
+
|
|
305
|
+
if (this.mouseMoved && isDefined(current) && isDefined(current.x1)) {
|
|
306
|
+
const newRetracements = retracements.concat({
|
|
307
|
+
...current,
|
|
308
|
+
x2: xyValue[0],
|
|
309
|
+
y2: xyValue[1],
|
|
310
|
+
selected: true,
|
|
311
|
+
appearance,
|
|
312
|
+
type,
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
this.setState(
|
|
316
|
+
{
|
|
317
|
+
current: null,
|
|
318
|
+
},
|
|
319
|
+
() => {
|
|
320
|
+
const { onComplete } = this.props;
|
|
321
|
+
if (onComplete !== undefined) {
|
|
322
|
+
onComplete(e, newRetracements, moreProps);
|
|
323
|
+
}
|
|
324
|
+
},
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
}
|