@tradingaction/interactive 2.0.13 → 2.0.16
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/lib/Rectangle.d.ts +86 -0
- package/lib/Rectangle.js +129 -0
- package/lib/Rectangle.js.map +1 -0
- package/lib/components/InteractiveRectangle.d.ts +67 -0
- package/lib/components/InteractiveRectangle.js +265 -0
- package/lib/components/InteractiveRectangle.js.map +1 -0
- package/lib/components/index.d.ts +1 -0
- package/lib/components/index.js +1 -0
- package/lib/components/index.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/wrapper/EachRectangle.d.ts +88 -0
- package/lib/wrapper/EachRectangle.js +145 -0
- package/lib/wrapper/EachRectangle.js.map +1 -0
- package/lib/wrapper/index.d.ts +1 -0
- package/lib/wrapper/index.js +1 -0
- package/lib/wrapper/index.js.map +1 -1
- package/package.json +4 -4
- package/src/Rectangle.tsx +309 -0
- package/src/components/InteractiveRectangle.tsx +401 -0
- package/src/components/index.ts +1 -0
- package/src/index.ts +1 -0
- package/src/wrapper/EachRectangle.tsx +352 -0
- package/src/wrapper/index.ts +1 -0
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getStrokeDasharrayCanvas,
|
|
3
|
+
getMouseCanvas,
|
|
4
|
+
GenericChartComponent,
|
|
5
|
+
noop,
|
|
6
|
+
strokeDashTypes,
|
|
7
|
+
} from "@tradingaction/core";
|
|
8
|
+
import * as React from "react";
|
|
9
|
+
|
|
10
|
+
export interface RectangleProps {
|
|
11
|
+
readonly topLeftX: any;
|
|
12
|
+
readonly topRightX: any;
|
|
13
|
+
readonly topLeftY: any;
|
|
14
|
+
readonly topRightY: any;
|
|
15
|
+
|
|
16
|
+
readonly bottomLeftX: any;
|
|
17
|
+
readonly bottomLeftY: any;
|
|
18
|
+
readonly bottomRightX: any;
|
|
19
|
+
readonly bottomRightY: any;
|
|
20
|
+
|
|
21
|
+
readonly interactiveCursorClass?: string;
|
|
22
|
+
readonly strokeStyle: string;
|
|
23
|
+
readonly strokeWidth?: number;
|
|
24
|
+
readonly strokeDasharray?: strokeDashTypes;
|
|
25
|
+
readonly type:
|
|
26
|
+
| "XLINE" // extends from -Infinity to +Infinity
|
|
27
|
+
| "RAY" // extends to +/-Infinity in one direction
|
|
28
|
+
| "LINE"; // extends between the set bounds
|
|
29
|
+
readonly onEdge1Drag?: any; // func
|
|
30
|
+
readonly onEdge2Drag?: any; // func
|
|
31
|
+
readonly onDragStart?: (e: React.MouseEvent, moreProps: any) => void;
|
|
32
|
+
readonly onDrag?: (e: React.MouseEvent, moreProps: any) => void;
|
|
33
|
+
readonly onDragComplete?: (e: React.MouseEvent, moreProps: any) => void;
|
|
34
|
+
readonly onHover?: (e: React.MouseEvent, moreProps: any) => void;
|
|
35
|
+
readonly onUnHover?: (e: React.MouseEvent, moreProps: any) => void;
|
|
36
|
+
readonly onDoubleClickWhenHover?: (e: React.MouseEvent, moreProps: any) => void;
|
|
37
|
+
readonly onClickWhenHover?: (e: React.MouseEvent, moreProps: any) => void;
|
|
38
|
+
readonly onClickOutside?: (e: React.MouseEvent, moreProps: any) => void;
|
|
39
|
+
readonly onContextMenuWhenHover?: (e: React.MouseEvent, moreProps: any) => void;
|
|
40
|
+
readonly defaultClassName?: string;
|
|
41
|
+
readonly r?: number;
|
|
42
|
+
readonly edgeFill?: string;
|
|
43
|
+
readonly edgeStroke?: string;
|
|
44
|
+
readonly edgeStrokeWidth?: number;
|
|
45
|
+
readonly rectangleFill?: string;
|
|
46
|
+
readonly withEdge?: boolean;
|
|
47
|
+
readonly tolerance?: number;
|
|
48
|
+
readonly selected?: boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export class InteractiveRectangle extends React.Component<RectangleProps> {
|
|
52
|
+
public static defaultProps = {
|
|
53
|
+
onEdge1Drag: noop,
|
|
54
|
+
onEdge2Drag: noop,
|
|
55
|
+
edgeStrokeWidth: 3,
|
|
56
|
+
edgeStroke: "#000000",
|
|
57
|
+
edgeFill: "#FFFFFF",
|
|
58
|
+
rectangleFill: 'transparent',
|
|
59
|
+
r: 10,
|
|
60
|
+
withEdge: false,
|
|
61
|
+
strokeWidth: 1,
|
|
62
|
+
strokeDasharray: "Solid",
|
|
63
|
+
children: noop,
|
|
64
|
+
tolerance: 7,
|
|
65
|
+
selected: false,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
public render() {
|
|
69
|
+
const { selected, interactiveCursorClass } = this.props;
|
|
70
|
+
const {
|
|
71
|
+
onDragStart,
|
|
72
|
+
onDrag,
|
|
73
|
+
onDragComplete,
|
|
74
|
+
onHover,
|
|
75
|
+
onUnHover,
|
|
76
|
+
onDoubleClickWhenHover,
|
|
77
|
+
onClickWhenHover,
|
|
78
|
+
onClickOutside,
|
|
79
|
+
onContextMenuWhenHover,
|
|
80
|
+
} = this.props;
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<GenericChartComponent
|
|
84
|
+
isHover={this.isHover}
|
|
85
|
+
canvasToDraw={getMouseCanvas}
|
|
86
|
+
canvasDraw={this.drawOnCanvas}
|
|
87
|
+
interactiveCursorClass={interactiveCursorClass}
|
|
88
|
+
selected={selected}
|
|
89
|
+
onDragStart={onDragStart}
|
|
90
|
+
onDrag={onDrag}
|
|
91
|
+
onDragComplete={onDragComplete}
|
|
92
|
+
onHover={onHover}
|
|
93
|
+
onUnHover={onUnHover}
|
|
94
|
+
drawOn={["mousemove", "pan", "drag"]}
|
|
95
|
+
onDoubleClickWhenHover={onDoubleClickWhenHover}
|
|
96
|
+
onClickWhenHover={onClickWhenHover}
|
|
97
|
+
onClickOutside={onClickOutside}
|
|
98
|
+
onContextMenuWhenHover={onContextMenuWhenHover}
|
|
99
|
+
/>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private readonly isHover = (moreProps: any) => {
|
|
104
|
+
const { tolerance, onHover } = this.props;
|
|
105
|
+
|
|
106
|
+
if (onHover !== undefined) {
|
|
107
|
+
const { topLeftX, topRightX, topLeftY, topRightY,
|
|
108
|
+
bottomLeftX, bottomLeftY, bottomRightX, bottomRightY,
|
|
109
|
+
type } = this.props;
|
|
110
|
+
const { mouseXY, xScale } = moreProps;
|
|
111
|
+
const {
|
|
112
|
+
chartConfig: { yScale },
|
|
113
|
+
} = moreProps;
|
|
114
|
+
|
|
115
|
+
const hovering = isHovering({
|
|
116
|
+
topLeftX,
|
|
117
|
+
topLeftY,
|
|
118
|
+
topRightX,
|
|
119
|
+
topRightY,
|
|
120
|
+
bottomLeftX, bottomLeftY, bottomRightX, bottomRightY,
|
|
121
|
+
mouseXY,
|
|
122
|
+
type,
|
|
123
|
+
tolerance,
|
|
124
|
+
xScale,
|
|
125
|
+
yScale,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
return hovering;
|
|
129
|
+
}
|
|
130
|
+
return false;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
private readonly drawOnCanvas = (ctx: CanvasRenderingContext2D, moreProps: any) => {
|
|
134
|
+
const {
|
|
135
|
+
strokeWidth = InteractiveRectangle.defaultProps.strokeWidth,
|
|
136
|
+
strokeDasharray,
|
|
137
|
+
strokeStyle,
|
|
138
|
+
rectangleFill
|
|
139
|
+
} = this.props;
|
|
140
|
+
const {
|
|
141
|
+
x1, y1, // top-left
|
|
142
|
+
x2, y2, // top-right
|
|
143
|
+
x1Bottom, y1Bottom, // bottom-left
|
|
144
|
+
x2Bottom, y2Bottom, // bottom-right
|
|
145
|
+
} = helper(this.props, moreProps);
|
|
146
|
+
|
|
147
|
+
// Set canvas styles
|
|
148
|
+
ctx.lineWidth = strokeWidth;
|
|
149
|
+
ctx.strokeStyle = strokeStyle || "transparent"; // Default to no border
|
|
150
|
+
ctx.fillStyle = rectangleFill || "rgba(0, 255, 0, 0.2)"; // Default fill color
|
|
151
|
+
ctx.setLineDash(getStrokeDasharrayCanvas(strokeDasharray));
|
|
152
|
+
|
|
153
|
+
// Begin rectangle path
|
|
154
|
+
ctx.beginPath();
|
|
155
|
+
|
|
156
|
+
// Start at top-left
|
|
157
|
+
ctx.moveTo(x1, y1);
|
|
158
|
+
|
|
159
|
+
// Draw to top-right
|
|
160
|
+
ctx.lineTo(x2, y2);
|
|
161
|
+
|
|
162
|
+
// Draw to bottom-right
|
|
163
|
+
ctx.lineTo(x2Bottom, y2Bottom);
|
|
164
|
+
|
|
165
|
+
// Draw to bottom-left
|
|
166
|
+
ctx.lineTo(x1Bottom, y1Bottom);
|
|
167
|
+
|
|
168
|
+
// Close the path back to top-left
|
|
169
|
+
ctx.closePath();
|
|
170
|
+
|
|
171
|
+
// ✅ Fill the rectangle
|
|
172
|
+
ctx.fill();
|
|
173
|
+
|
|
174
|
+
// ✅ Stroke the outline (optional)
|
|
175
|
+
if (strokeStyle !== "transparent" && strokeWidth > 0) {
|
|
176
|
+
ctx.stroke();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function isHovering({ topLeftX, topLeftY, topRightX, topRightY, bottomLeftX, bottomLeftY, bottomRightX, bottomRightY, mouseXY, type, tolerance, xScale, yScale }: any) {
|
|
183
|
+
const line = generateRectangle({
|
|
184
|
+
type,
|
|
185
|
+
start: [topLeftX, topLeftY],
|
|
186
|
+
end: [topRightX, topRightY],
|
|
187
|
+
startBottom: [bottomLeftX, bottomLeftY],
|
|
188
|
+
endBottom: [bottomRightX, bottomRightY],
|
|
189
|
+
xScale,
|
|
190
|
+
yScale,
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
const start = [xScale(line.x1), yScale(line.y1)];
|
|
194
|
+
const end = [xScale(line.x2), yScale(line.y2)];
|
|
195
|
+
|
|
196
|
+
const m = getSlope(start, end);
|
|
197
|
+
const [mouseX, mouseY] = mouseXY;
|
|
198
|
+
|
|
199
|
+
if (m !== undefined) {
|
|
200
|
+
const b = getYIntercept(m, end);
|
|
201
|
+
const y = m * mouseX + b;
|
|
202
|
+
|
|
203
|
+
return (
|
|
204
|
+
mouseY < y + tolerance &&
|
|
205
|
+
mouseY > y - tolerance &&
|
|
206
|
+
mouseX > Math.min(start[0], end[0]) - tolerance &&
|
|
207
|
+
mouseX < Math.max(start[0], end[0]) + tolerance
|
|
208
|
+
);
|
|
209
|
+
} else {
|
|
210
|
+
return (
|
|
211
|
+
mouseY >= Math.min(start[1], end[1]) &&
|
|
212
|
+
mouseY <= Math.max(start[1], end[1]) &&
|
|
213
|
+
mouseX < start[0] + tolerance &&
|
|
214
|
+
mouseX > start[0] - tolerance
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function helper(props: any, moreProps: any) {
|
|
220
|
+
const { topLeftX, topRightX, topLeftY, topRightY, bottomLeftX, bottomLeftY, bottomRightX, bottomRightY, type } = props;
|
|
221
|
+
|
|
222
|
+
const {
|
|
223
|
+
xScale,
|
|
224
|
+
chartConfig: { yScale },
|
|
225
|
+
} = moreProps;
|
|
226
|
+
|
|
227
|
+
const modLine = generateRectangle({
|
|
228
|
+
type,
|
|
229
|
+
start: [topLeftX, topLeftY],
|
|
230
|
+
end: [topRightX, topRightY],
|
|
231
|
+
startBottom: [bottomLeftX, bottomLeftY],
|
|
232
|
+
endBottom: [bottomRightX, bottomRightY],
|
|
233
|
+
xScale,
|
|
234
|
+
yScale,
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
const x1 = xScale(modLine.x1);
|
|
238
|
+
const y1 = yScale(modLine.y1);
|
|
239
|
+
const x2 = xScale(modLine.x2);
|
|
240
|
+
const y2 = yScale(modLine.y2);
|
|
241
|
+
|
|
242
|
+
const x1Bottom = xScale(modLine.x1Bottom);
|
|
243
|
+
const y1Bottom = yScale(modLine.y1Bottom);
|
|
244
|
+
const x2Bottom = xScale(modLine.x2Bottom);
|
|
245
|
+
const y2Bottom = yScale(modLine.y2Bottom);
|
|
246
|
+
|
|
247
|
+
return {
|
|
248
|
+
x1,//Top (from Trendline cloning process)
|
|
249
|
+
y1,//Top (from Trendline cloning process)
|
|
250
|
+
x2,//Top (from Trendline cloning process)
|
|
251
|
+
y2,//Top (from Trendline cloning process)
|
|
252
|
+
|
|
253
|
+
x1Bottom,
|
|
254
|
+
y1Bottom,
|
|
255
|
+
x2Bottom,
|
|
256
|
+
y2Bottom,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function getSlope(start: any, end: any) {
|
|
261
|
+
const m /* slope */ = end[0] === start[0] ? undefined : (end[1] - start[1]) / (end[0] - start[0]);
|
|
262
|
+
return m;
|
|
263
|
+
}
|
|
264
|
+
function getYIntercept(m: any, end: any) {
|
|
265
|
+
const b /* y intercept */ = -1 * m * end[0] + end[1];
|
|
266
|
+
return b;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export function generateRectangle({ type, start, end, startBottom, endBottom, xScale, yScale }: any) {
|
|
270
|
+
const m /* slope */ = getSlope(start, end);
|
|
271
|
+
const b /* y intercept */ = getYIntercept(m, start);
|
|
272
|
+
|
|
273
|
+
switch (type) {
|
|
274
|
+
case "XLINE":
|
|
275
|
+
return getXLineCoordinates({
|
|
276
|
+
start,
|
|
277
|
+
end,
|
|
278
|
+
startBottom, endBottom,
|
|
279
|
+
xScale,
|
|
280
|
+
yScale,
|
|
281
|
+
m,
|
|
282
|
+
b,
|
|
283
|
+
});
|
|
284
|
+
case "RAY":
|
|
285
|
+
return getRayCoordinates({
|
|
286
|
+
start,
|
|
287
|
+
end,
|
|
288
|
+
startBottom, endBottom,
|
|
289
|
+
xScale,
|
|
290
|
+
yScale,
|
|
291
|
+
m,
|
|
292
|
+
b,
|
|
293
|
+
});
|
|
294
|
+
default:
|
|
295
|
+
case "LINE":
|
|
296
|
+
return getLineCoordinates({
|
|
297
|
+
start,
|
|
298
|
+
end,
|
|
299
|
+
startBottom, endBottom,
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function getXLineCoordinates({ start, end, startBottom, endBottom, xScale, yScale, m, b }: any) {
|
|
305
|
+
const [xBegin, xFinish] = xScale.domain();
|
|
306
|
+
const [yBegin, yFinish] = yScale.domain();
|
|
307
|
+
|
|
308
|
+
if (end[0] === start[0]) {
|
|
309
|
+
return {
|
|
310
|
+
x1: end[0],
|
|
311
|
+
y1: yBegin,
|
|
312
|
+
x2: end[0],
|
|
313
|
+
y2: yFinish,
|
|
314
|
+
|
|
315
|
+
x1Bottom: 0,//Todo next
|
|
316
|
+
y1Bottom: 0,
|
|
317
|
+
x2Bottom: 0,
|
|
318
|
+
y2Bottom: 0,
|
|
319
|
+
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
const [x1, x2] = end[0] > start[0] ? [xBegin, xFinish] : [xFinish, xBegin];
|
|
323
|
+
|
|
324
|
+
return {
|
|
325
|
+
x1,
|
|
326
|
+
y1: m * x1 + b,
|
|
327
|
+
x2,
|
|
328
|
+
y2: m * x2 + b,
|
|
329
|
+
|
|
330
|
+
x1Bottom: 0,//Todo next
|
|
331
|
+
y1Bottom: 0,
|
|
332
|
+
x2Bottom: 0,
|
|
333
|
+
y2Bottom: 0,
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function getRayCoordinates({ start, end, startBottom, endBottom, xScale, yScale, m, b }: any) {
|
|
338
|
+
const [xBegin, xFinish] = xScale.domain();
|
|
339
|
+
const [yBegin, yFinish] = yScale.domain();
|
|
340
|
+
|
|
341
|
+
const x1 = start[0];
|
|
342
|
+
if (end[0] === start[0]) {
|
|
343
|
+
return {
|
|
344
|
+
x1,
|
|
345
|
+
y1: start[1],
|
|
346
|
+
x2: x1,
|
|
347
|
+
y2: end[1] > start[1] ? yFinish : yBegin,
|
|
348
|
+
|
|
349
|
+
x1Bottom: 0,//Todo next
|
|
350
|
+
y1Bottom: 0,
|
|
351
|
+
x2Bottom: 0,
|
|
352
|
+
y2Bottom: 0,
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const x2 = end[0] > start[0] ? xFinish : xBegin;
|
|
357
|
+
|
|
358
|
+
return {
|
|
359
|
+
x1,
|
|
360
|
+
y1: m * x1 + b,
|
|
361
|
+
x2,
|
|
362
|
+
y2: m * x2 + b,
|
|
363
|
+
|
|
364
|
+
x1Bottom: 0,//Todo next
|
|
365
|
+
y1Bottom: 0,
|
|
366
|
+
x2Bottom: 0,
|
|
367
|
+
y2Bottom: 0,
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function getLineCoordinates({ start, end, startBottom, endBottom, }: any) {
|
|
372
|
+
const [x1, y1] = start;
|
|
373
|
+
const [x2, y2] = end;
|
|
374
|
+
const [x1Bottom, y1Bottom] = startBottom;
|
|
375
|
+
const [x2Bottom, y2Bottom] = endBottom;
|
|
376
|
+
if (end[0] === start[0] || endBottom[0] === startBottom[0]) {
|
|
377
|
+
return {
|
|
378
|
+
x1,
|
|
379
|
+
y1: start[1],
|
|
380
|
+
x2: x1,
|
|
381
|
+
y2: end[1],
|
|
382
|
+
|
|
383
|
+
x1Bottom,
|
|
384
|
+
y1Bottom: startBottom[1],
|
|
385
|
+
x2Bottom,
|
|
386
|
+
y2Bottom: endBottom[1],
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return {
|
|
391
|
+
x1,
|
|
392
|
+
y1,
|
|
393
|
+
x2,
|
|
394
|
+
y2,
|
|
395
|
+
|
|
396
|
+
x1Bottom,
|
|
397
|
+
y1Bottom,
|
|
398
|
+
x2Bottom,
|
|
399
|
+
y2Bottom,
|
|
400
|
+
};
|
|
401
|
+
}
|
package/src/components/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ export * from "./ClickableShape";
|
|
|
4
4
|
export * from "./GannFan";
|
|
5
5
|
export * from "./HoverTextNearMouse";
|
|
6
6
|
export * from "./InteractiveStraightLine";
|
|
7
|
+
export * from "./InteractiveRectangle";
|
|
7
8
|
export * from "./InteractiveText";
|
|
8
9
|
export * from "./InteractiveYCoordinate";
|
|
9
10
|
export * from "./LinearRegressionChannelWithArea";
|
package/src/index.ts
CHANGED