@tradingaction/coordinates 2.0.0

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 (51) hide show
  1. package/LICENSE +24 -0
  2. package/README.md +5 -0
  3. package/lib/CrossHairCursor.d.ts +22 -0
  4. package/lib/CrossHairCursor.js +76 -0
  5. package/lib/CrossHairCursor.js.map +1 -0
  6. package/lib/CurrentCoordinate.d.ts +31 -0
  7. package/lib/CurrentCoordinate.js +54 -0
  8. package/lib/CurrentCoordinate.js.map +1 -0
  9. package/lib/Cursor.d.ts +31 -0
  10. package/lib/Cursor.js +122 -0
  11. package/lib/Cursor.js.map +1 -0
  12. package/lib/EdgeCoordinate.d.ts +32 -0
  13. package/lib/EdgeCoordinate.js +151 -0
  14. package/lib/EdgeCoordinate.js.map +1 -0
  15. package/lib/EdgeCoordinateV2.d.ts +3 -0
  16. package/lib/EdgeCoordinateV2.js +138 -0
  17. package/lib/EdgeCoordinateV2.js.map +1 -0
  18. package/lib/EdgeCoordinateV3.d.ts +3 -0
  19. package/lib/EdgeCoordinateV3.js +176 -0
  20. package/lib/EdgeCoordinateV3.js.map +1 -0
  21. package/lib/EdgeIndicator.d.ts +55 -0
  22. package/lib/EdgeIndicator.js +85 -0
  23. package/lib/EdgeIndicator.js.map +1 -0
  24. package/lib/MouseCoordinateX.d.ts +49 -0
  25. package/lib/MouseCoordinateX.js +85 -0
  26. package/lib/MouseCoordinateX.js.map +1 -0
  27. package/lib/MouseCoordinateXV2.d.ts +61 -0
  28. package/lib/MouseCoordinateXV2.js +92 -0
  29. package/lib/MouseCoordinateXV2.js.map +1 -0
  30. package/lib/MouseCoordinateY.d.ts +68 -0
  31. package/lib/MouseCoordinateY.js +91 -0
  32. package/lib/MouseCoordinateY.js.map +1 -0
  33. package/lib/PriceCoordinate.d.ts +53 -0
  34. package/lib/PriceCoordinate.js +81 -0
  35. package/lib/PriceCoordinate.js.map +1 -0
  36. package/lib/index.d.ts +8 -0
  37. package/lib/index.js +9 -0
  38. package/lib/index.js.map +1 -0
  39. package/package.json +49 -0
  40. package/src/CrossHairCursor.tsx +116 -0
  41. package/src/CurrentCoordinate.tsx +95 -0
  42. package/src/Cursor.tsx +174 -0
  43. package/src/EdgeCoordinate.tsx +239 -0
  44. package/src/EdgeCoordinateV2.tsx +204 -0
  45. package/src/EdgeCoordinateV3.tsx +284 -0
  46. package/src/EdgeIndicator.tsx +161 -0
  47. package/src/MouseCoordinateX.tsx +127 -0
  48. package/src/MouseCoordinateXV2.tsx +161 -0
  49. package/src/MouseCoordinateY.tsx +141 -0
  50. package/src/PriceCoordinate.tsx +121 -0
  51. package/src/index.ts +8 -0
@@ -0,0 +1,284 @@
1
+ import * as React from "react";
2
+
3
+ import { getStrokeDasharray, getStrokeDasharrayCanvas, isDefined } from "@tradingaction/core";
4
+
5
+ export const renderSVG = (props: any) => {
6
+ const { className } = props;
7
+
8
+ const edge = helper(props);
9
+ if (edge === null) {
10
+ return null;
11
+ }
12
+
13
+ let line;
14
+ let coordinateBase;
15
+ let coordinate;
16
+
17
+ if (edge.line !== undefined) {
18
+ line = (
19
+ <line
20
+ className="react-financial-charts-cross-hair"
21
+ stroke={edge.line.stroke}
22
+ strokeDasharray={getStrokeDasharray(edge.line.strokeDasharray)}
23
+ x1={edge.line.x1}
24
+ y1={edge.line.y1}
25
+ x2={edge.line.x2}
26
+ y2={edge.line.y2}
27
+ />
28
+ );
29
+ }
30
+ if (edge.coordinate !== undefined && edge.coordinateBase !== undefined) {
31
+ const { rectWidth, rectHeight, arrowWidth } = edge.coordinateBase;
32
+
33
+ const path =
34
+ edge.orient === "left"
35
+ ? `M0,0L0,${rectHeight}L${rectWidth},${rectHeight}L${rectWidth + arrowWidth},10L${rectWidth},0L0,0L0,0`
36
+ : `M0,${arrowWidth}L${arrowWidth},${rectHeight}L${rectWidth + arrowWidth},${rectHeight}L${
37
+ rectWidth + arrowWidth
38
+ },0L${arrowWidth},0L0,${arrowWidth}`;
39
+
40
+ coordinateBase =
41
+ edge.orient === "left" || edge.orient === "right" ? (
42
+ <g key={1} transform={`translate(${edge.coordinateBase.edgeXRect},${edge.coordinateBase.edgeYRect})`}>
43
+ <path
44
+ d={path}
45
+ className="react-financial-charts-text-background"
46
+ height={rectHeight}
47
+ width={rectWidth}
48
+ stroke={edge.coordinateBase.stroke}
49
+ strokeLinejoin="miter"
50
+ strokeWidth={edge.coordinateBase.strokeWidth}
51
+ fill={edge.coordinateBase.fill}
52
+ />
53
+ </g>
54
+ ) : (
55
+ <rect
56
+ key={1}
57
+ className="react-financial-charts-text-background"
58
+ x={edge.coordinateBase.edgeXRect}
59
+ y={edge.coordinateBase.edgeYRect}
60
+ height={rectHeight}
61
+ width={rectWidth}
62
+ fill={edge.coordinateBase.fill}
63
+ />
64
+ );
65
+
66
+ coordinate = (
67
+ <text
68
+ key={2}
69
+ x={edge.coordinate.edgeXText}
70
+ y={edge.coordinate.edgeYText}
71
+ textAnchor={edge.coordinate.textAnchor}
72
+ fontFamily={edge.coordinate.fontFamily}
73
+ fontSize={edge.coordinate.fontSize}
74
+ dy=".32em"
75
+ fill={edge.coordinate.textFill}
76
+ >
77
+ {edge.coordinate.displayCoordinate}
78
+ </text>
79
+ );
80
+ }
81
+ return (
82
+ <g className={className}>
83
+ {line}
84
+ {coordinateBase}
85
+ {coordinate}
86
+ </g>
87
+ );
88
+ };
89
+
90
+ const helper = (props: any) => {
91
+ const {
92
+ coordinate: displayCoordinate,
93
+ show,
94
+ type,
95
+ orient,
96
+ edgeAt,
97
+ hideLine,
98
+ lineStrokeDasharray,
99
+ fill,
100
+ fontFamily,
101
+ fontSize,
102
+ textFill,
103
+ lineStroke,
104
+ stroke,
105
+ strokeWidth,
106
+ arrowWidth,
107
+ rectWidth,
108
+ rectHeight,
109
+ rectRadius,
110
+ x1,
111
+ y1,
112
+ x2,
113
+ y2,
114
+ dx,
115
+ } = props;
116
+
117
+ if (!show) {
118
+ return null;
119
+ }
120
+
121
+ let coordinateBase;
122
+ let coordinate;
123
+ if (displayCoordinate !== undefined) {
124
+ const textAnchor = "middle";
125
+
126
+ let edgeXRect;
127
+ let edgeYRect;
128
+ let edgeXText;
129
+ let edgeYText;
130
+
131
+ if (type === "horizontal") {
132
+ edgeXRect = dx + (orient === "right" ? edgeAt + 1 : edgeAt - rectWidth - 1);
133
+ edgeYRect = y1 - rectHeight / 2 - strokeWidth;
134
+ edgeXText = dx + (orient === "right" ? edgeAt + rectWidth / 2 : edgeAt - rectWidth / 2);
135
+ edgeYText = y1;
136
+ } else {
137
+ const dy = orient === "bottom" ? strokeWidth - 1 : -strokeWidth + 1;
138
+ edgeXRect = x1 - rectWidth / 2;
139
+ edgeYRect = (orient === "bottom" ? edgeAt : edgeAt - rectHeight) + dy;
140
+ edgeXText = x1;
141
+ edgeYText = (orient === "bottom" ? edgeAt + rectHeight / 2 : edgeAt - rectHeight / 2) + dy;
142
+ }
143
+
144
+ coordinateBase = {
145
+ edgeXRect,
146
+ edgeYRect,
147
+ rectHeight: rectHeight + strokeWidth,
148
+ rectWidth,
149
+ rectRadius,
150
+ fill,
151
+ arrowWidth,
152
+ stroke,
153
+ strokeWidth,
154
+ };
155
+ coordinate = {
156
+ edgeXText,
157
+ edgeYText,
158
+ textAnchor,
159
+ fontFamily,
160
+ fontSize,
161
+ textFill,
162
+ displayCoordinate,
163
+ };
164
+ }
165
+
166
+ const line = hideLine
167
+ ? undefined
168
+ : {
169
+ stroke: lineStroke,
170
+ strokeDasharray: lineStrokeDasharray,
171
+ x1,
172
+ y1,
173
+ x2,
174
+ y2,
175
+ };
176
+
177
+ return {
178
+ coordinateBase,
179
+ coordinate,
180
+ line,
181
+ orient,
182
+ };
183
+ };
184
+
185
+ export const drawOnCanvas = (ctx: CanvasRenderingContext2D, props: any) => {
186
+ const { coordinate, fitToText, fontSize, fontFamily, rectWidth } = props;
187
+
188
+ ctx.font = `${fontSize}px ${fontFamily}`;
189
+ ctx.textBaseline = "middle";
190
+
191
+ let width = rectWidth;
192
+ if (fitToText) {
193
+ width = Math.round(ctx.measureText(coordinate).width + 10);
194
+ }
195
+
196
+ const edge = helper({ ...props, rectWidth: width });
197
+ if (edge === null) {
198
+ return;
199
+ }
200
+
201
+ if (edge.line !== undefined && isDefined(edge.line)) {
202
+ const dashArray = getStrokeDasharrayCanvas(edge.line.strokeDasharray);
203
+ ctx.setLineDash(dashArray);
204
+ ctx.strokeStyle = edge.line.stroke;
205
+ ctx.lineWidth = 1;
206
+ ctx.beginPath();
207
+ ctx.moveTo(edge.line.x1, edge.line.y1);
208
+ ctx.lineTo(edge.line.x2, edge.line.y2);
209
+ ctx.stroke();
210
+ }
211
+
212
+ ctx.setLineDash([]);
213
+
214
+ if (edge.coordinateBase !== undefined) {
215
+ const { arrowWidth, rectWidth, rectHeight, rectRadius } = edge.coordinateBase;
216
+
217
+ ctx.fillStyle = edge.coordinateBase.fill;
218
+ if (edge.coordinateBase.stroke !== undefined) {
219
+ ctx.strokeStyle = edge.coordinateBase.stroke;
220
+ ctx.lineWidth = edge.coordinateBase.strokeWidth;
221
+ }
222
+
223
+ let x = edge.coordinateBase.edgeXRect;
224
+ const y = edge.coordinateBase.edgeYRect;
225
+ const halfHeight = rectHeight / 2;
226
+
227
+ ctx.beginPath();
228
+
229
+ if (arrowWidth > 0 && edge.orient === "right") {
230
+ x -= arrowWidth;
231
+ ctx.moveTo(x, y + halfHeight);
232
+ ctx.lineTo(x + arrowWidth, y);
233
+ ctx.lineTo(x + rectWidth + arrowWidth, y);
234
+ ctx.lineTo(x + rectWidth + arrowWidth, y + rectHeight);
235
+ ctx.lineTo(x + arrowWidth, y + rectHeight);
236
+ ctx.closePath();
237
+ } else if (arrowWidth > 0 && edge.orient === "left") {
238
+ ctx.moveTo(x, y);
239
+ ctx.lineTo(x + rectWidth, y);
240
+ ctx.lineTo(x + rectWidth + arrowWidth, y + halfHeight);
241
+ ctx.lineTo(x + rectWidth, y + rectHeight);
242
+ ctx.lineTo(x, y + rectHeight);
243
+ ctx.closePath();
244
+ } else if (rectRadius) {
245
+ roundRect(ctx, x - 0.5, y - 0.5, rectWidth, rectHeight, 3);
246
+ } else {
247
+ ctx.rect(x - 0.5, y, rectWidth, rectHeight);
248
+ }
249
+
250
+ ctx.fill();
251
+
252
+ if (edge.coordinateBase.stroke !== undefined) {
253
+ ctx.stroke();
254
+ }
255
+
256
+ if (edge.coordinate !== undefined) {
257
+ ctx.fillStyle = edge.coordinate.textFill;
258
+ ctx.textAlign =
259
+ edge.coordinate.textAnchor === "middle" ? "center" : (edge.coordinate.textAnchor as CanvasTextAlign);
260
+ ctx.fillText(edge.coordinate.displayCoordinate, edge.coordinate.edgeXText, edge.coordinate.edgeYText);
261
+ }
262
+ }
263
+ };
264
+
265
+ const roundRect = (
266
+ ctx: CanvasRenderingContext2D,
267
+ x: number,
268
+ y: number,
269
+ width: number,
270
+ height: number,
271
+ radius: number,
272
+ ) => {
273
+ ctx.beginPath();
274
+ ctx.moveTo(x + radius, y);
275
+ ctx.lineTo(x + width - radius, y);
276
+ ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
277
+ ctx.lineTo(x + width, y + height - radius);
278
+ ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
279
+ ctx.lineTo(x + radius, y + height);
280
+ ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
281
+ ctx.lineTo(x, y + radius);
282
+ ctx.quadraticCurveTo(x, y, x + radius, y);
283
+ ctx.closePath();
284
+ };
@@ -0,0 +1,161 @@
1
+ import { format } from "d3-format";
2
+ import * as React from "react";
3
+ import {
4
+ first,
5
+ functor,
6
+ getAxisCanvas,
7
+ GenericChartComponent,
8
+ last,
9
+ noop,
10
+ strokeDashTypes,
11
+ } from "@tradingaction/core";
12
+ import { drawOnCanvas } from "./EdgeCoordinateV3";
13
+
14
+ export interface EdgeIndicatorProps {
15
+ readonly arrowWidth?: number;
16
+ readonly displayFormat?: (n: number) => string;
17
+ readonly edgeAt?: "left" | "right";
18
+ readonly fill?: string | ((datum: any) => string);
19
+ readonly fitToText?: boolean;
20
+ readonly fontFamily?: string;
21
+ readonly fontSize?: number;
22
+ readonly fullWidth?: boolean;
23
+ readonly itemType: "first" | "last";
24
+ readonly lineStroke?: string | ((datum: any) => string);
25
+ readonly lineStrokeDasharray?: strokeDashTypes;
26
+ readonly orient?: "left" | "right";
27
+ readonly rectHeight?: number;
28
+ readonly rectWidth?: number;
29
+ readonly stroke?: string | ((datum: any) => string);
30
+ readonly textFill?: string | ((datum: any) => string);
31
+ readonly type?: "horizontal";
32
+ readonly yAccessor: (data: any) => number | undefined;
33
+ readonly yAxisPad?: number;
34
+ }
35
+
36
+ export class EdgeIndicator extends React.Component<EdgeIndicatorProps> {
37
+ public static defaultProps = {
38
+ fitToText: false,
39
+ lineStroke: "#000000",
40
+ lineOpacity: 1,
41
+ lineStrokeDasharray: "ShortDot",
42
+ orient: "right",
43
+ displayFormat: format(".2f"),
44
+ edgeAt: "right",
45
+ yAxisPad: 0,
46
+ rectHeight: 20,
47
+ rectWidth: 50,
48
+ arrowWidth: 0,
49
+ fontFamily: "-apple-system, system-ui, Roboto, 'Helvetica Neue', Ubuntu, sans-serif",
50
+ fontSize: 13,
51
+ dx: 0,
52
+ hideLine: false,
53
+ fill: "#8a8a8a",
54
+ opacity: 1,
55
+ stroke: noop,
56
+ strokeOpacity: 1,
57
+ strokeWidth: 1,
58
+ textFill: "#FFFFFF",
59
+ type: "horizontal",
60
+ };
61
+
62
+ public render() {
63
+ return (
64
+ <GenericChartComponent
65
+ edgeClip
66
+ clip={false}
67
+ canvasDraw={this.drawOnCanvas}
68
+ canvasToDraw={getAxisCanvas}
69
+ drawOn={["pan"]}
70
+ />
71
+ );
72
+ }
73
+
74
+ private readonly drawOnCanvas = (ctx: CanvasRenderingContext2D, moreProps: any) => {
75
+ const edge = this.helper(this.props, moreProps);
76
+ if (edge === undefined) {
77
+ return;
78
+ }
79
+
80
+ const props = {
81
+ ...this.props,
82
+ ...edge,
83
+ };
84
+
85
+ drawOnCanvas(ctx, props);
86
+ };
87
+
88
+ private readonly helper = (props: EdgeIndicatorProps, moreProps: any) => {
89
+ const { itemType, yAccessor } = props;
90
+
91
+ const { plotData } = moreProps;
92
+
93
+ const item = itemType === "first" ? first(plotData, yAccessor) : last(plotData, yAccessor);
94
+
95
+ const edge = item !== undefined ? this.getEdge(moreProps, item) : undefined;
96
+
97
+ return edge;
98
+ };
99
+
100
+ private readonly getEdge = (moreProps: any, item: any) => {
101
+ const {
102
+ fontFamily,
103
+ fontSize,
104
+ type: edgeType,
105
+ displayFormat = EdgeIndicator.defaultProps.displayFormat,
106
+ edgeAt,
107
+ yAxisPad = EdgeIndicator.defaultProps.yAxisPad,
108
+ orient,
109
+ lineStroke,
110
+ yAccessor,
111
+ fill,
112
+ fullWidth,
113
+ textFill,
114
+ rectHeight,
115
+ rectWidth,
116
+ arrowWidth,
117
+ stroke,
118
+ } = this.props;
119
+
120
+ const {
121
+ xScale,
122
+ chartConfig: { yScale },
123
+ xAccessor,
124
+ width,
125
+ } = moreProps;
126
+
127
+ const yValue = yAccessor(item);
128
+ if (yValue === undefined) {
129
+ return undefined;
130
+ }
131
+
132
+ const xValue = xAccessor(item);
133
+
134
+ const x1 = fullWidth ? 0 : Math.round(xScale(xValue));
135
+ const y1 = Math.round(yScale(yValue));
136
+
137
+ const [left, right] = [0, width];
138
+ const edgeX = edgeAt === "left" ? left - yAxisPad : right + yAxisPad;
139
+
140
+ return {
141
+ coordinate: displayFormat(yValue),
142
+ show: true,
143
+ type: edgeType,
144
+ orient,
145
+ edgeAt: edgeX,
146
+ fill: functor(fill)(item),
147
+ lineStroke: functor(lineStroke)(item),
148
+ stroke: functor(stroke)(item),
149
+ fontFamily,
150
+ fontSize,
151
+ textFill: functor(textFill)(item),
152
+ rectHeight,
153
+ rectWidth,
154
+ arrowWidth,
155
+ x1,
156
+ y1,
157
+ x2: right,
158
+ y2: y1,
159
+ };
160
+ };
161
+ }
@@ -0,0 +1,127 @@
1
+ import * as React from "react";
2
+ import { isNotDefined, getMouseCanvas, GenericChartComponent } from "@tradingaction/core";
3
+ import { drawOnCanvas } from "./EdgeCoordinateV3";
4
+
5
+ export interface MouseCoordinateXProps {
6
+ readonly at?: "bottom" | "top";
7
+ readonly customX: (props: MouseCoordinateXProps, moreProps: any) => { x: number; coordinate: string };
8
+ readonly displayFormat: (item: any) => string;
9
+ readonly fill?: string;
10
+ readonly fitToText?: boolean;
11
+ readonly fontFamily?: string;
12
+ readonly fontSize?: number;
13
+ readonly opacity?: number;
14
+ readonly orient?: "bottom" | "top";
15
+ readonly rectRadius?: number;
16
+ readonly rectWidth?: number;
17
+ readonly rectHeight?: number;
18
+ readonly snapX?: boolean;
19
+ readonly stroke?: string;
20
+ readonly strokeOpacity?: number;
21
+ readonly strokeWidth?: number;
22
+ readonly textFill?: string;
23
+ readonly yAxisPad?: number;
24
+ }
25
+
26
+ const defaultCustomX = (props: MouseCoordinateXProps, moreProps: any) => {
27
+ const { xScale, xAccessor, currentItem, mouseXY } = moreProps;
28
+ const { snapX } = props;
29
+ const x = snapX ? xScale(xAccessor(currentItem)) : mouseXY[0];
30
+
31
+ const { displayXAccessor } = moreProps;
32
+ const { displayFormat } = props;
33
+ const coordinate = snapX ? displayFormat(displayXAccessor(currentItem)) : displayFormat(xScale.invert(x));
34
+ return { x, coordinate };
35
+ };
36
+
37
+ export class MouseCoordinateX extends React.Component<MouseCoordinateXProps> {
38
+ public static defaultProps = {
39
+ at: "bottom",
40
+ customX: defaultCustomX,
41
+ fill: "#4C525E",
42
+ fitToText: true,
43
+ fontFamily: "-apple-system, system-ui, Roboto, 'Helvetica Neue', Ubuntu, sans-serif",
44
+ fontSize: 13,
45
+ opacity: 1,
46
+ orient: "bottom",
47
+ rectWidth: 80,
48
+ rectHeight: 20,
49
+ snapX: true,
50
+ strokeOpacity: 1,
51
+ strokeWidth: 1,
52
+ textFill: "#FFFFFF",
53
+ yAxisPad: 0,
54
+ };
55
+
56
+ public render() {
57
+ return (
58
+ <GenericChartComponent
59
+ clip={false}
60
+ canvasDraw={this.drawOnCanvas}
61
+ canvasToDraw={getMouseCanvas}
62
+ drawOn={["mousemove", "pan", "drag"]}
63
+ />
64
+ );
65
+ }
66
+
67
+ private readonly drawOnCanvas = (ctx: CanvasRenderingContext2D, moreProps: any) => {
68
+ const props = this.helper(this.props, moreProps);
69
+ if (props === null) {
70
+ return;
71
+ }
72
+
73
+ drawOnCanvas(ctx, props);
74
+ };
75
+
76
+ private readonly helper = (props: MouseCoordinateXProps, moreProps: any) => {
77
+ const {
78
+ show,
79
+ currentItem,
80
+ chartConfig: { height },
81
+ } = moreProps;
82
+
83
+ if (isNotDefined(currentItem)) {
84
+ return null;
85
+ }
86
+
87
+ const { customX, orient, at, rectRadius, rectWidth, rectHeight, stroke, strokeOpacity, strokeWidth } = props;
88
+ const { fill, opacity, fitToText, fontFamily, fontSize, textFill } = props;
89
+
90
+ const edgeAt = at === "bottom" ? height : 0;
91
+
92
+ const { x, coordinate } = customX(props, moreProps);
93
+
94
+ const type = "vertical";
95
+ const y1 = 0;
96
+ const y2 = height;
97
+ const hideLine = true;
98
+
99
+ const coordinateProps = {
100
+ coordinate,
101
+ fitToText,
102
+ show,
103
+ type,
104
+ orient,
105
+ edgeAt,
106
+ hideLine,
107
+ fill,
108
+ opacity,
109
+ fontFamily,
110
+ fontSize,
111
+ textFill,
112
+ stroke,
113
+ strokeOpacity,
114
+ strokeWidth,
115
+ rectWidth,
116
+ rectHeight,
117
+ rectRadius,
118
+ arrowWidth: 0,
119
+ x1: x,
120
+ x2: x,
121
+ y1,
122
+ y2,
123
+ };
124
+
125
+ return coordinateProps;
126
+ };
127
+ }