1 | import React, { Component } from "react";
|
2 |
|
3 | import { LinearGradient, Line, Text, Defs, Stop } from "react-native-svg";
|
4 |
|
5 | class AbstractChart extends Component {
|
6 | calcScaler = data => {
|
7 | if (this.props.fromZero) {
|
8 | return Math.max(...data, 0) - Math.min(...data, 0) || 1;
|
9 | } else {
|
10 | return Math.max(...data) - Math.min(...data) || 1;
|
11 | }
|
12 | };
|
13 |
|
14 | calcBaseHeight = (data, height) => {
|
15 | const min = Math.min(...data);
|
16 | const max = Math.max(...data);
|
17 | if (min >= 0 && max >= 0) {
|
18 | return height;
|
19 | } else if (min < 0 && max <= 0) {
|
20 | return 0;
|
21 | } else if (min < 0 && max > 0) {
|
22 | return (height * max) / this.calcScaler(data);
|
23 | }
|
24 | };
|
25 |
|
26 | calcHeight = (val, data, height) => {
|
27 | const max = Math.max(...data);
|
28 | const min = Math.min(...data);
|
29 | if (min < 0 && max > 0) {
|
30 | return height * (val / this.calcScaler(data));
|
31 | } else if (min >= 0 && max >= 0) {
|
32 | return this.props.fromZero
|
33 | ? height * (val / this.calcScaler(data))
|
34 | : height * ((val - min) / this.calcScaler(data));
|
35 | } else if (min < 0 && max <= 0) {
|
36 | return this.props.fromZero
|
37 | ? height * (val / this.calcScaler(data))
|
38 | : height * ((val - max) / this.calcScaler(data));
|
39 | }
|
40 | };
|
41 |
|
42 | getPropsForBackgroundLines() {
|
43 | const { propsForBackgroundLines = {} } = this.props.chartConfig;
|
44 | return {
|
45 | stroke: this.props.chartConfig.color(0.2),
|
46 | strokeDasharray: "5, 10",
|
47 | strokeWidth: 1,
|
48 | ...propsForBackgroundLines
|
49 | };
|
50 | }
|
51 |
|
52 | getPropsForLabels() {
|
53 | const {
|
54 | propsForLabels = {},
|
55 | color,
|
56 | labelColor = color
|
57 | } = this.props.chartConfig;
|
58 | return {
|
59 | fontSize: 12,
|
60 | fill: labelColor(0.8),
|
61 | ...propsForLabels
|
62 | };
|
63 | }
|
64 |
|
65 | renderHorizontalLines = config => {
|
66 | const { count, width, height, paddingTop, paddingRight } = config;
|
67 | return [...new Array(count)].map((_, i) => {
|
68 | return (
|
69 | <Line
|
70 | key={Math.random()}
|
71 | x1={paddingRight}
|
72 | y1={(height / 4) * i + paddingTop}
|
73 | x2={width}
|
74 | y2={(height / 4) * i + paddingTop}
|
75 | {...this.getPropsForBackgroundLines()}
|
76 | />
|
77 | );
|
78 | });
|
79 | };
|
80 |
|
81 | renderHorizontalLine = config => {
|
82 | const { width, height, paddingTop, paddingRight } = config;
|
83 | return (
|
84 | <Line
|
85 | key={Math.random()}
|
86 | x1={paddingRight}
|
87 | y1={height - height / 4 + paddingTop}
|
88 | x2={width}
|
89 | y2={height - height / 4 + paddingTop}
|
90 | {...this.getPropsForBackgroundLines()}
|
91 | />
|
92 | );
|
93 | };
|
94 |
|
95 | renderHorizontalLabels = config => {
|
96 | const {
|
97 | count,
|
98 | data,
|
99 | height,
|
100 | paddingTop,
|
101 | paddingRight,
|
102 | horizontalLabelRotation = 0,
|
103 | formatYLabel = yLabel => yLabel
|
104 | } = config;
|
105 | const {
|
106 | yAxisLabel = "",
|
107 | yAxisSuffix = "",
|
108 | yLabelsOffset = 12,
|
109 | chartConfig
|
110 | } = this.props;
|
111 | const { decimalPlaces = 2 } = chartConfig;
|
112 | return [...new Array(count)].map((_, i) => {
|
113 | let yLabel;
|
114 |
|
115 | if (count === 1) {
|
116 | yLabel = `${yAxisLabel}${formatYLabel(
|
117 | data[0].toFixed(decimalPlaces)
|
118 | )}${yAxisSuffix}`;
|
119 | } else {
|
120 | const label = this.props.fromZero
|
121 | ? (this.calcScaler(data) / (count - 1)) * i + Math.min(...data, 0)
|
122 | : (this.calcScaler(data) / (count - 1)) * i + Math.min(...data);
|
123 | yLabel = `${yAxisLabel}${formatYLabel(
|
124 | label.toFixed(decimalPlaces)
|
125 | )}${yAxisSuffix}`;
|
126 | }
|
127 |
|
128 | const x = paddingRight - yLabelsOffset;
|
129 | const y =
|
130 | count === 1 && this.props.fromZero
|
131 | ? paddingTop + 4
|
132 | : (height * 3) / 4 - ((height - paddingTop) / count) * i + 12;
|
133 | return (
|
134 | <Text
|
135 | rotation={horizontalLabelRotation}
|
136 | origin={`${x}, ${y}`}
|
137 | key={Math.random()}
|
138 | x={x}
|
139 | textAnchor="end"
|
140 | y={y}
|
141 | {...this.getPropsForLabels()}
|
142 | >
|
143 | {yLabel}
|
144 | </Text>
|
145 | );
|
146 | });
|
147 | };
|
148 |
|
149 | renderVerticalLabels = config => {
|
150 | const {
|
151 | labels = [],
|
152 | width,
|
153 | height,
|
154 | paddingRight,
|
155 | paddingTop,
|
156 | horizontalOffset = 0,
|
157 | stackedBar = false,
|
158 | verticalLabelRotation = 0,
|
159 | formatXLabel = xLabel => xLabel
|
160 | } = config;
|
161 | const {
|
162 | xAxisLabel = "",
|
163 | xLabelsOffset = 0,
|
164 | hidePointsAtIndex = []
|
165 | } = this.props;
|
166 | const fontSize = 12;
|
167 | let fac = 1;
|
168 | if (stackedBar) {
|
169 | fac = 0.71;
|
170 | }
|
171 | return labels.map((label, i) => {
|
172 | if (hidePointsAtIndex.includes(i)) {
|
173 | return null;
|
174 | }
|
175 | const x =
|
176 | (((width - paddingRight) / labels.length) * i +
|
177 | paddingRight +
|
178 | horizontalOffset) *
|
179 | fac;
|
180 | const y = (height * 3) / 4 + paddingTop + fontSize * 2 + xLabelsOffset;
|
181 | return (
|
182 | <Text
|
183 | origin={`${x}, ${y}`}
|
184 | rotation={verticalLabelRotation}
|
185 | key={Math.random()}
|
186 | x={x}
|
187 | y={y}
|
188 | textAnchor={verticalLabelRotation === 0 ? "middle" : "start"}
|
189 | {...this.getPropsForLabels()}
|
190 | >
|
191 | {`${formatXLabel(label)}${xAxisLabel}`}
|
192 | </Text>
|
193 | );
|
194 | });
|
195 | };
|
196 |
|
197 | renderVerticalLines = config => {
|
198 | const { data, width, height, paddingTop, paddingRight } = config;
|
199 | return [...new Array(data.length)].map((_, i) => {
|
200 | return (
|
201 | <Line
|
202 | key={Math.random()}
|
203 | x1={Math.floor(
|
204 | ((width - paddingRight) / data.length) * i + paddingRight
|
205 | )}
|
206 | y1={0}
|
207 | x2={Math.floor(
|
208 | ((width - paddingRight) / data.length) * i + paddingRight
|
209 | )}
|
210 | y2={height - height / 4 + paddingTop}
|
211 | {...this.getPropsForBackgroundLines()}
|
212 | />
|
213 | );
|
214 | });
|
215 | };
|
216 |
|
217 | renderVerticalLine = config => {
|
218 | const { height, paddingTop, paddingRight } = config;
|
219 | return (
|
220 | <Line
|
221 | key={Math.random()}
|
222 | x1={Math.floor(paddingRight)}
|
223 | y1={0}
|
224 | x2={Math.floor(paddingRight)}
|
225 | y2={height - height / 4 + paddingTop}
|
226 | {...this.getPropsForBackgroundLines()}
|
227 | />
|
228 | );
|
229 | };
|
230 |
|
231 | renderDefs = config => {
|
232 | const {
|
233 | width,
|
234 | height,
|
235 | backgroundGradientFrom,
|
236 | backgroundGradientTo
|
237 | } = config;
|
238 | const fromOpacity = config.hasOwnProperty("backgroundGradientFromOpacity")
|
239 | ? config.backgroundGradientFromOpacity
|
240 | : 1.0;
|
241 | const toOpacity = config.hasOwnProperty("backgroundGradientToOpacity")
|
242 | ? config.backgroundGradientToOpacity
|
243 | : 1.0;
|
244 |
|
245 | const fillShadowGradient = config.hasOwnProperty("fillShadowGradient")
|
246 | ? config.fillShadowGradient
|
247 | : this.props.chartConfig.color();
|
248 |
|
249 | const fillShadowGradientOpacity = config.hasOwnProperty(
|
250 | "fillShadowGradientOpacity"
|
251 | )
|
252 | ? config.fillShadowGradientOpacity
|
253 | : 0.1;
|
254 |
|
255 | return (
|
256 | <Defs>
|
257 | <LinearGradient
|
258 | id="backgroundGradient"
|
259 | x1="0"
|
260 | y1={height}
|
261 | x2={width}
|
262 | y2={0}
|
263 | >
|
264 | <Stop
|
265 | offset="0"
|
266 | stopColor={backgroundGradientFrom}
|
267 | stopOpacity={fromOpacity}
|
268 | />
|
269 | <Stop
|
270 | offset="1"
|
271 | stopColor={backgroundGradientTo}
|
272 | stopOpacity={toOpacity}
|
273 | />
|
274 | </LinearGradient>
|
275 | <LinearGradient
|
276 | id="fillShadowGradient"
|
277 | x1={0}
|
278 | y1={0}
|
279 | x2={0}
|
280 | y2={height}
|
281 | >
|
282 | <Stop
|
283 | offset="0"
|
284 | stopColor={fillShadowGradient}
|
285 | stopOpacity={fillShadowGradientOpacity}
|
286 | />
|
287 | <Stop offset="1" stopColor={fillShadowGradient} stopOpacity="0" />
|
288 | </LinearGradient>
|
289 | </Defs>
|
290 | );
|
291 | };
|
292 | }
|
293 |
|
294 | export default AbstractChart;
|