@tradingaction/series 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.
Files changed (119) hide show
  1. package/LICENSE +24 -0
  2. package/README.md +5 -0
  3. package/lib/AlternateDataSeries.d.ts +5 -0
  4. package/lib/AlternateDataSeries.js +16 -0
  5. package/lib/AlternateDataSeries.js.map +1 -0
  6. package/lib/AlternatingFillAreaSeries.d.ts +77 -0
  7. package/lib/AlternatingFillAreaSeries.js +69 -0
  8. package/lib/AlternatingFillAreaSeries.js.map +1 -0
  9. package/lib/AreaOnlySeries.d.ts +42 -0
  10. package/lib/AreaOnlySeries.js +53 -0
  11. package/lib/AreaOnlySeries.js.map +1 -0
  12. package/lib/AreaSeries.d.ts +46 -0
  13. package/lib/AreaSeries.js +21 -0
  14. package/lib/AreaSeries.js.map +1 -0
  15. package/lib/BarSeries.d.ts +36 -0
  16. package/lib/BarSeries.js +91 -0
  17. package/lib/BarSeries.js.map +1 -0
  18. package/lib/BollingerSeries.d.ts +33 -0
  19. package/lib/BollingerSeries.js +59 -0
  20. package/lib/BollingerSeries.js.map +1 -0
  21. package/lib/CandlestickSeries.d.ts +65 -0
  22. package/lib/CandlestickSeries.js +115 -0
  23. package/lib/CandlestickSeries.js.map +1 -0
  24. package/lib/ElderRaySeries.d.ts +45 -0
  25. package/lib/ElderRaySeries.js +66 -0
  26. package/lib/ElderRaySeries.js.map +1 -0
  27. package/lib/GroupedBarSeries.d.ts +33 -0
  28. package/lib/GroupedBarSeries.js +22 -0
  29. package/lib/GroupedBarSeries.js.map +1 -0
  30. package/lib/KagiSeries.d.ts +45 -0
  31. package/lib/KagiSeries.js +108 -0
  32. package/lib/KagiSeries.js.map +1 -0
  33. package/lib/LineSeries.d.ts +84 -0
  34. package/lib/LineSeries.js +102 -0
  35. package/lib/LineSeries.js.map +1 -0
  36. package/lib/MACDSeries.d.ts +55 -0
  37. package/lib/MACDSeries.js +62 -0
  38. package/lib/MACDSeries.js.map +1 -0
  39. package/lib/OHLCSeries.d.ts +32 -0
  40. package/lib/OHLCSeries.js +67 -0
  41. package/lib/OHLCSeries.js.map +1 -0
  42. package/lib/OverlayBarSeries.d.ts +35 -0
  43. package/lib/OverlayBarSeries.js +71 -0
  44. package/lib/OverlayBarSeries.js.map +1 -0
  45. package/lib/PointAndFigureSeries.d.ts +33 -0
  46. package/lib/PointAndFigureSeries.js +92 -0
  47. package/lib/PointAndFigureSeries.js.map +1 -0
  48. package/lib/RSISeries.d.ts +68 -0
  49. package/lib/RSISeries.js +82 -0
  50. package/lib/RSISeries.js.map +1 -0
  51. package/lib/RenkoSeries.d.ts +42 -0
  52. package/lib/RenkoSeries.js +65 -0
  53. package/lib/RenkoSeries.js.map +1 -0
  54. package/lib/SARSeries.d.ts +34 -0
  55. package/lib/SARSeries.js +69 -0
  56. package/lib/SARSeries.js.map +1 -0
  57. package/lib/SVGComponent.d.ts +8 -0
  58. package/lib/SVGComponent.js +9 -0
  59. package/lib/SVGComponent.js.map +1 -0
  60. package/lib/ScatterSeries.d.ts +24 -0
  61. package/lib/ScatterSeries.js +60 -0
  62. package/lib/ScatterSeries.js.map +1 -0
  63. package/lib/StackedBarSeries.d.ts +44 -0
  64. package/lib/StackedBarSeries.js +157 -0
  65. package/lib/StackedBarSeries.js.map +1 -0
  66. package/lib/StochasticSeries.d.ts +39 -0
  67. package/lib/StochasticSeries.js +42 -0
  68. package/lib/StochasticSeries.js.map +1 -0
  69. package/lib/StraightLine.d.ts +38 -0
  70. package/lib/StraightLine.js +81 -0
  71. package/lib/StraightLine.js.map +1 -0
  72. package/lib/VolumeProfileSeries.d.ts +45 -0
  73. package/lib/VolumeProfileSeries.js +161 -0
  74. package/lib/VolumeProfileSeries.js.map +1 -0
  75. package/lib/index.d.ts +23 -0
  76. package/lib/index.js +24 -0
  77. package/lib/index.js.map +1 -0
  78. package/lib/markers/CircleMarker.d.ts +25 -0
  79. package/lib/markers/CircleMarker.js +35 -0
  80. package/lib/markers/CircleMarker.js.map +1 -0
  81. package/lib/markers/SquareMarker.d.ts +25 -0
  82. package/lib/markers/SquareMarker.js +37 -0
  83. package/lib/markers/SquareMarker.js.map +1 -0
  84. package/lib/markers/TriangleMarker.d.ts +27 -0
  85. package/lib/markers/TriangleMarker.js +89 -0
  86. package/lib/markers/TriangleMarker.js.map +1 -0
  87. package/lib/markers/index.d.ts +3 -0
  88. package/lib/markers/index.js +4 -0
  89. package/lib/markers/index.js.map +1 -0
  90. package/package.json +52 -0
  91. package/src/AlternateDataSeries.tsx +29 -0
  92. package/src/AlternatingFillAreaSeries.tsx +159 -0
  93. package/src/AreaOnlySeries.tsx +106 -0
  94. package/src/AreaSeries.tsx +95 -0
  95. package/src/BarSeries.tsx +151 -0
  96. package/src/BollingerSeries.tsx +90 -0
  97. package/src/CandlestickSeries.tsx +188 -0
  98. package/src/ElderRaySeries.tsx +99 -0
  99. package/src/GroupedBarSeries.tsx +48 -0
  100. package/src/KagiSeries.tsx +155 -0
  101. package/src/LineSeries.tsx +223 -0
  102. package/src/MACDSeries.tsx +110 -0
  103. package/src/OHLCSeries.tsx +116 -0
  104. package/src/OverlayBarSeries.tsx +115 -0
  105. package/src/PointAndFigureSeries.tsx +140 -0
  106. package/src/RSISeries.tsx +158 -0
  107. package/src/RenkoSeries.tsx +118 -0
  108. package/src/SARSeries.tsx +111 -0
  109. package/src/SVGComponent.tsx +13 -0
  110. package/src/ScatterSeries.tsx +105 -0
  111. package/src/StackedBarSeries.tsx +272 -0
  112. package/src/StochasticSeries.tsx +70 -0
  113. package/src/StraightLine.tsx +166 -0
  114. package/src/VolumeProfileSeries.tsx +247 -0
  115. package/src/index.ts +23 -0
  116. package/src/markers/CircleMarker.tsx +69 -0
  117. package/src/markers/SquareMarker.tsx +71 -0
  118. package/src/markers/TriangleMarker.tsx +131 -0
  119. package/src/markers/index.ts +3 -0
@@ -0,0 +1,247 @@
1
+ import { ascending, descending, histogram as d3Histogram, max, merge, rollup, sum, zip } from "d3-array";
2
+ import { scaleLinear } from "d3-scale";
3
+ import * as React from "react";
4
+ import {
5
+ accumulatingWindow,
6
+ functor,
7
+ head,
8
+ identity,
9
+ getAxisCanvas,
10
+ GenericChartComponent,
11
+ last,
12
+ } from "@tradingaction/core";
13
+
14
+ export interface VolumeProfileSeriesProps {
15
+ readonly absoluteChange: (datum: any) => number;
16
+ readonly bins: number;
17
+ readonly bySession?: boolean;
18
+ readonly fill?: (widthType: { type: "up" | "down"; width: number }) => string;
19
+ readonly maxProfileWidthPercent: number;
20
+ readonly orient?: "left" | "right";
21
+ readonly partialStartOK?: boolean;
22
+ readonly partialEndOK?: boolean;
23
+ readonly sessionBackGround?: string;
24
+ readonly sessionStart: ({ d, i, plotData }: any) => boolean;
25
+ readonly showSessionBackground?: boolean;
26
+ readonly source: (d: number, i: number, data: ArrayLike<number>) => number;
27
+ readonly stroke?: string;
28
+ readonly volume: (datum: any) => number;
29
+ }
30
+
31
+ export class VolumeProfileSeries extends React.Component<VolumeProfileSeriesProps> {
32
+ public static defaultProps = {
33
+ absoluteChange: (d: any) => d.absoluteChange,
34
+ bins: 20,
35
+ bySession: false,
36
+ fill: ({ type }: { type: "up" | "down"; width: number }) =>
37
+ type === "up" ? "rgba(38, 166, 154, 0.5)" : "rgba(239, 83, 80, 0.5)",
38
+ maxProfileWidthPercent: 50,
39
+ orient: "left",
40
+ partialStartOK: true,
41
+ partialEndOK: true,
42
+ sessionBackGround: "rgba(70, 130, 180, 0.3)",
43
+ sessionStart: ({ d, i, plotData }: any) => i > 0 && plotData[i - 1].date.getMonth() !== d.date.getMonth(),
44
+ showSessionBackground: false,
45
+ source: (d: any) => d.close,
46
+ stroke: "#FFFFFF",
47
+ volume: (d: any) => d.volume,
48
+ };
49
+
50
+ public render() {
51
+ return <GenericChartComponent canvasDraw={this.drawOnCanvas} canvasToDraw={getAxisCanvas} drawOn={["pan"]} />;
52
+ }
53
+
54
+ private readonly drawOnCanvas = (ctx: CanvasRenderingContext2D, moreProps: any) => {
55
+ const { xAccessor, width } = moreProps;
56
+
57
+ const { rects, sessionBg } = this.helper(this.props, moreProps, xAccessor, width);
58
+
59
+ this.drawOnCanvasContext(ctx, rects, sessionBg);
60
+ };
61
+
62
+ private readonly drawOnCanvasContext = (ctx: CanvasRenderingContext2D, rects: any[], sessionBg: any[]) => {
63
+ const { sessionBackGround, showSessionBackground } = this.props;
64
+
65
+ if (showSessionBackground) {
66
+ if (sessionBackGround !== undefined) {
67
+ ctx.fillStyle = sessionBackGround;
68
+ }
69
+
70
+ sessionBg.forEach((each: any) => {
71
+ const { x, y, height, width } = each;
72
+
73
+ ctx.beginPath();
74
+ ctx.rect(x, y, width, height);
75
+ ctx.closePath();
76
+ ctx.fill();
77
+ });
78
+ }
79
+
80
+ rects.forEach((each: any) => {
81
+ const { x, y, height, w1, w2, stroke1, stroke2, fill1, fill2 } = each;
82
+
83
+ if (w1 > 0) {
84
+ ctx.fillStyle = fill1;
85
+ if (stroke1 !== "none") {
86
+ ctx.strokeStyle = stroke1;
87
+ }
88
+
89
+ ctx.beginPath();
90
+ ctx.rect(x, y, w1, height);
91
+ ctx.closePath();
92
+ ctx.fill();
93
+
94
+ if (stroke1 !== "none") {
95
+ ctx.stroke();
96
+ }
97
+ }
98
+
99
+ if (w2 > 0) {
100
+ ctx.fillStyle = fill2;
101
+ if (stroke2 !== "none") {
102
+ ctx.strokeStyle = stroke2;
103
+ }
104
+
105
+ ctx.beginPath();
106
+ ctx.rect(x + w1, y, w2, height);
107
+ ctx.closePath();
108
+ ctx.fill();
109
+
110
+ if (stroke2 !== "none") {
111
+ ctx.stroke();
112
+ }
113
+ }
114
+ });
115
+ };
116
+
117
+ private readonly helper = (props: VolumeProfileSeriesProps, moreProps: any, xAccessor: any, width: number) => {
118
+ const {
119
+ xScale: realXScale,
120
+ chartConfig: { yScale },
121
+ plotData,
122
+ } = moreProps;
123
+
124
+ const {
125
+ sessionStart,
126
+ bySession,
127
+ partialStartOK,
128
+ partialEndOK,
129
+ bins,
130
+ maxProfileWidthPercent,
131
+ source,
132
+ volume,
133
+ absoluteChange,
134
+ orient,
135
+ fill,
136
+ stroke,
137
+ } = props;
138
+
139
+ const sessionBuilder = accumulatingWindow()
140
+ .discardTillStart(!partialStartOK)
141
+ .discardTillEnd(!partialEndOK)
142
+ .accumulateTill((d: any, i: any) => {
143
+ return sessionStart({ d, i, ...moreProps });
144
+ })
145
+ .accumulator(identity);
146
+
147
+ const dx = plotData.length > 1 ? realXScale(xAccessor(plotData[1])) - realXScale(xAccessor(head(plotData))) : 0;
148
+
149
+ const sessions = bySession ? sessionBuilder(plotData) : [plotData];
150
+
151
+ const allRects = sessions.map((session: any) => {
152
+ const begin = bySession ? realXScale(xAccessor(head(session))) : 0;
153
+ const finish = bySession ? realXScale(xAccessor(last(session))) : width;
154
+ const sessionWidth = finish - begin + dx;
155
+
156
+ const histogram2 = d3Histogram().value(source).thresholds(bins);
157
+
158
+ const rolledup = (data: any[]) => {
159
+ const sortFunction = orient === "right" ? descending : ascending;
160
+
161
+ const sortedData = data.sort((a, b) => sortFunction(a.direction, b.direction));
162
+
163
+ return rollup(
164
+ sortedData,
165
+ (leaves) => sum<any>(leaves, (d) => d.volume),
166
+ (d) => d.direction,
167
+ );
168
+ };
169
+
170
+ const values = histogram2(session);
171
+
172
+ const volumeInBins = values
173
+ .map((arr) =>
174
+ arr.map((d) => {
175
+ return absoluteChange(d) > 0
176
+ ? { direction: "up", volume: volume(d) }
177
+ : { direction: "down", volume: volume(d) };
178
+ }),
179
+ )
180
+ .map((arr) => Array.from(rolledup(arr)));
181
+
182
+ const volumeValues = volumeInBins.map((each) => sum(each.map((d) => d[1])));
183
+
184
+ const base = (xScaleD: any) => head(xScaleD.range());
185
+
186
+ const [start, end] =
187
+ orient === "right"
188
+ ? [begin, begin + (sessionWidth * maxProfileWidthPercent) / 100]
189
+ : [finish, finish - (sessionWidth * (100 - maxProfileWidthPercent)) / 100];
190
+
191
+ const xScale = scaleLinear()
192
+ .domain([0, max(volumeValues)!])
193
+ .range([start, end]);
194
+
195
+ const totalVolumes = volumeInBins.map((volumes) => {
196
+ const totalVolume = sum<any>(volumes, (d) => d[1]);
197
+ const totalVolumeX = xScale(totalVolume);
198
+ const widthLocal = base(xScale) - totalVolumeX;
199
+ const x = widthLocal < 0 ? totalVolumeX + widthLocal : totalVolumeX;
200
+
201
+ const ws = volumes.map((d) => {
202
+ return {
203
+ type: d[0],
204
+ width: (d[1] * Math.abs(widthLocal)) / totalVolume,
205
+ };
206
+ });
207
+
208
+ return { x, ws, totalVolumeX };
209
+ });
210
+
211
+ // @ts-ignore
212
+ const rects = zip(values, totalVolumes)
213
+ // @ts-ignore
214
+ .map(([d, { x, ws }]) => {
215
+ const w1 = ws[0] || { type: "up", width: 0 };
216
+ const w2 = ws[1] || { type: "down", width: 0 };
217
+
218
+ return {
219
+ y: yScale(d.x1),
220
+ height: yScale(d.x1) - yScale(d.x0),
221
+ x,
222
+ width,
223
+ w1: w1.width,
224
+ w2: w2.width,
225
+ stroke1: functor(stroke)(w1),
226
+ stroke2: functor(stroke)(w2),
227
+ fill1: functor(fill)(w1),
228
+ fill2: functor(fill)(w2),
229
+ };
230
+ });
231
+
232
+ const sessionBg = {
233
+ x: begin,
234
+ y: last(rects).y,
235
+ height: head(rects).y - last(rects).y + head(rects).height,
236
+ width: sessionWidth,
237
+ };
238
+
239
+ return { rects, sessionBg };
240
+ });
241
+
242
+ return {
243
+ rects: merge<any>(allRects.map((d: any) => d.rects)),
244
+ sessionBg: allRects.map((d: any) => d.sessionBg),
245
+ };
246
+ };
247
+ }
package/src/index.ts ADDED
@@ -0,0 +1,23 @@
1
+ export * from "./AlternateDataSeries";
2
+ export * from "./AlternatingFillAreaSeries";
3
+ export * from "./AreaOnlySeries";
4
+ export * from "./AreaSeries";
5
+ export * from "./markers";
6
+ export * from "./LineSeries";
7
+ export * from "./CandlestickSeries";
8
+ export * from "./OHLCSeries";
9
+ export * from "./BarSeries";
10
+ export { StackedBarSeries, StackedBarSeriesProps } from "./StackedBarSeries";
11
+ export * from "./GroupedBarSeries";
12
+ export * from "./KagiSeries";
13
+ export * from "./PointAndFigureSeries";
14
+ export * from "./RenkoSeries";
15
+ export * from "./MACDSeries";
16
+ export * from "./BollingerSeries";
17
+ export * from "./RSISeries";
18
+ export * from "./StochasticSeries";
19
+ export * from "./ElderRaySeries";
20
+ export * from "./VolumeProfileSeries";
21
+ export * from "./ScatterSeries";
22
+ export * from "./StraightLine";
23
+ export * from "./SARSeries";
@@ -0,0 +1,69 @@
1
+ import { functor } from "@tradingaction/core";
2
+ import * as React from "react";
3
+
4
+ export interface CircleMarkerProps {
5
+ readonly className?: string;
6
+ readonly fillStyle?: string;
7
+ readonly point: {
8
+ x: number;
9
+ y: number;
10
+ datum: any;
11
+ };
12
+ readonly r: number | ((datum: any) => number);
13
+ readonly strokeStyle?: string;
14
+ readonly strokeWidth?: number;
15
+ }
16
+
17
+ export class CircleMarker extends React.Component<CircleMarkerProps> {
18
+ public static defaultProps = {
19
+ fillStyle: "#4682B4",
20
+ className: "react-financial-charts-marker-circle",
21
+ };
22
+
23
+ public static drawOnCanvas = (
24
+ props: CircleMarkerProps,
25
+ point: { x: number; y: number; datum: unknown },
26
+ ctx: CanvasRenderingContext2D,
27
+ ) => {
28
+ const { strokeStyle, fillStyle, r, strokeWidth } = props;
29
+
30
+ if (strokeStyle !== undefined) {
31
+ ctx.strokeStyle = strokeStyle;
32
+ }
33
+ if (strokeWidth !== undefined) {
34
+ ctx.lineWidth = strokeWidth;
35
+ }
36
+ if (fillStyle !== undefined) {
37
+ ctx.fillStyle = fillStyle;
38
+ }
39
+
40
+ const { datum, x, y } = point;
41
+
42
+ const radius = functor(r)(datum);
43
+
44
+ ctx.moveTo(x, y);
45
+ ctx.beginPath();
46
+ ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
47
+ ctx.fill();
48
+ if (strokeStyle !== undefined) {
49
+ ctx.stroke();
50
+ }
51
+ };
52
+
53
+ public render() {
54
+ const { className, strokeStyle, strokeWidth, fillStyle, point, r } = this.props;
55
+ const radius = functor(r)(point.datum);
56
+
57
+ return (
58
+ <circle
59
+ className={className}
60
+ cx={point.x}
61
+ cy={point.y}
62
+ stroke={strokeStyle}
63
+ strokeWidth={strokeWidth}
64
+ fill={fillStyle}
65
+ r={radius}
66
+ />
67
+ );
68
+ }
69
+ }
@@ -0,0 +1,71 @@
1
+ import * as React from "react";
2
+ import { functor } from "@tradingaction/core";
3
+
4
+ export interface SquareProps {
5
+ readonly className?: string;
6
+ readonly fillStyle?: string;
7
+ readonly point: {
8
+ x: number;
9
+ y: number;
10
+ datum: any;
11
+ };
12
+ readonly strokeStyle?: string;
13
+ readonly strokeWidth?: number;
14
+ readonly width: number | ((datum: any) => number);
15
+ }
16
+
17
+ export class Square extends React.Component<SquareProps> {
18
+ public static defaultProps = {
19
+ fillStyle: "#4682B4",
20
+ className: "react-financial-charts-marker-rect",
21
+ };
22
+
23
+ public static drawOnCanvas = (
24
+ props: SquareProps,
25
+ point: { x: number; y: number; datum: unknown },
26
+ ctx: CanvasRenderingContext2D,
27
+ ) => {
28
+ const { strokeStyle, fillStyle, strokeWidth, width } = props;
29
+
30
+ if (strokeStyle !== undefined) {
31
+ ctx.strokeStyle = strokeStyle;
32
+ }
33
+ if (strokeWidth !== undefined) {
34
+ ctx.lineWidth = strokeWidth;
35
+ }
36
+ if (fillStyle !== undefined) {
37
+ ctx.fillStyle = fillStyle;
38
+ }
39
+
40
+ const w = functor(width)(point.datum);
41
+ const x = point.x - w / 2;
42
+ const y = point.y - w / 2;
43
+ ctx.beginPath();
44
+ ctx.rect(x, y, w, w);
45
+ ctx.fill();
46
+
47
+ if (strokeStyle !== undefined) {
48
+ ctx.stroke();
49
+ }
50
+ };
51
+
52
+ public render() {
53
+ const { className, strokeStyle, strokeWidth, fillStyle, point, width } = this.props;
54
+ const w = functor(width)(point.datum);
55
+ const x = point.x - w / 2;
56
+ const y = point.y - w / 2;
57
+
58
+ return (
59
+ <rect
60
+ className={className}
61
+ x={x}
62
+ y={y}
63
+ stroke={strokeStyle}
64
+ strokeWidth={strokeWidth}
65
+ fill={fillStyle}
66
+ width={w}
67
+ height={w}
68
+ />
69
+ );
70
+ }
71
+ }
@@ -0,0 +1,131 @@
1
+ import * as React from "react";
2
+ import { functor } from "@tradingaction/core";
3
+
4
+ export interface TriangleProps {
5
+ readonly className?: string;
6
+ readonly direction?: "top" | "bottom" | "left" | "right" | "hide" | ((datum: any) => any);
7
+ readonly fillStyle?: string | ((datum: any) => string);
8
+ readonly point: {
9
+ x: number;
10
+ y: number;
11
+ datum: any;
12
+ };
13
+ readonly strokeStyle?: string | ((datum: any) => string);
14
+ readonly strokeWidth?: number;
15
+ readonly width?: number | ((datum: any) => number);
16
+ }
17
+
18
+ export class Triangle extends React.Component<TriangleProps> {
19
+ public static defaultProps = {
20
+ direction: "top",
21
+ fillStyle: "#4682B4",
22
+ className: "react-financial-charts-marker-triangle",
23
+ };
24
+
25
+ public static drawOnCanvas = (
26
+ props: TriangleProps,
27
+ point: { x: number; y: number; datum: unknown },
28
+ ctx: CanvasRenderingContext2D,
29
+ ) => {
30
+ const { fillStyle, strokeStyle, strokeWidth, width } = props;
31
+
32
+ if (strokeStyle !== undefined) {
33
+ ctx.strokeStyle = functor(strokeStyle)(point.datum);
34
+ }
35
+ if (strokeWidth !== undefined) {
36
+ ctx.lineWidth = strokeWidth;
37
+ }
38
+ if (fillStyle !== undefined) {
39
+ ctx.fillStyle = functor(fillStyle)(point.datum);
40
+ }
41
+
42
+ const w = functor(width)(point.datum);
43
+ const { x, y } = point;
44
+ const { innerOpposite, innerHypotenuse } = getTrianglePoints(w);
45
+ const rotationDeg = getRotationInDegrees(props, point);
46
+
47
+ ctx.beginPath();
48
+ ctx.moveTo(x, y - innerHypotenuse);
49
+ ctx.lineTo(x + w / 2, y + innerOpposite);
50
+ ctx.lineTo(x - w / 2, y + innerOpposite);
51
+
52
+ // TODO: rotation does not work
53
+ // example: https://gist.github.com/geoffb/6392450
54
+ if (rotationDeg !== null && rotationDeg !== 0) {
55
+ ctx.save();
56
+ ctx.translate(x, y);
57
+ ctx.rotate((rotationDeg * Math.PI) / 180); // 45 degrees
58
+ ctx.fill();
59
+ ctx.restore();
60
+ }
61
+ ctx.fill();
62
+
63
+ if (strokeStyle !== undefined) {
64
+ ctx.stroke();
65
+ }
66
+ };
67
+
68
+ public render() {
69
+ const { className, fillStyle, strokeStyle, strokeWidth, point, width } = this.props;
70
+
71
+ const rotation = getRotationInDegrees(this.props, point);
72
+ if (rotation == null) {
73
+ return null;
74
+ }
75
+
76
+ const fillColor = functor(fillStyle)(point.datum);
77
+ const strokeColor = functor(strokeStyle)(point.datum);
78
+
79
+ const w = functor(width)(point.datum);
80
+ const { x, y } = point;
81
+ const { innerOpposite, innerHypotenuse } = getTrianglePoints(w);
82
+ const points = `
83
+ ${x} ${y - innerHypotenuse},
84
+ ${x + w / 2} ${y + innerOpposite},
85
+ ${x - w / 2} ${y + innerOpposite}
86
+ `;
87
+
88
+ return (
89
+ <polygon
90
+ className={className}
91
+ points={points}
92
+ stroke={strokeColor}
93
+ strokeWidth={strokeWidth}
94
+ fill={fillColor}
95
+ transform={rotation !== 0 ? `rotate(${rotation}, ${x}, ${y})` : undefined}
96
+ />
97
+ );
98
+ }
99
+ }
100
+
101
+ const getTrianglePoints = (width: number) => {
102
+ const innerHypotenuse = (width / 2) * (1 / Math.cos((30 * Math.PI) / 180));
103
+ const innerOpposite = (width / 2) * (1 / Math.tan((60 * Math.PI) / 180));
104
+ return {
105
+ innerOpposite,
106
+ innerHypotenuse,
107
+ };
108
+ };
109
+
110
+ const getRotationInDegrees = (props: TriangleProps, point: any) => {
111
+ const { direction = Triangle.defaultProps.direction } = props;
112
+
113
+ const directionVal = functor(direction)(point.datum);
114
+ if (directionVal === "hide") {
115
+ return null;
116
+ }
117
+
118
+ let rotate = 0;
119
+ switch (directionVal) {
120
+ case "bottom":
121
+ rotate = 180;
122
+ break;
123
+ case "left":
124
+ rotate = -90;
125
+ break;
126
+ case "right":
127
+ rotate = 90;
128
+ break;
129
+ }
130
+ return rotate;
131
+ };
@@ -0,0 +1,3 @@
1
+ export * from "./CircleMarker";
2
+ export * from "./SquareMarker";
3
+ export * from "./TriangleMarker";