├── .watchmanconfig
├── .npmignore
├── screenshots
└── README.png
├── .buckconfig
├── .babelrc
├── dist
├── util.js
├── constants.js
├── Circle.js
├── xAxis.js
├── Grid.js
├── yAxis.js
├── PieChart.js
├── BarChart.js
├── Wedge.js
├── LineChart.js
└── Chart.js
├── src
├── constants.js
├── util.js
├── Circle.js
├── xAxis.js
├── BarChart.js
├── PieChart.js
├── Grid.js
├── yAxis.js
├── Wedge.js
├── LineChart.js
└── Chart.js
├── .editorconfig
├── .gitignore
├── LICENSE
├── .eslintrc
├── react-native-chart.flow.js
├── .flowconfig
├── package.json
└── README.md
/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | screenshots
2 |
--------------------------------------------------------------------------------
/screenshots/README.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tomauty/react-native-chart/HEAD/screenshots/README.png
--------------------------------------------------------------------------------
/.buckconfig:
--------------------------------------------------------------------------------
1 |
2 | [android]
3 | target = Google Inc.:Google APIs:23
4 |
5 | [maven_repositories]
6 | central = https://repo1.maven.org/maven2
7 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "retainLines": true,
3 | "compact": true,
4 | "comments": false,
5 | "presets": ["react-native"],
6 | "plugins": ["transform-flow-strip-types"],
7 | "sourceMaps": false
8 | }
9 |
--------------------------------------------------------------------------------
/dist/util.js:
--------------------------------------------------------------------------------
1 | Object.defineProperty(exports,"__esModule",{value:true});exports.
2 |
3 | uniqueValuesInDataSet=uniqueValuesInDataSet;function uniqueValuesInDataSet(data){
4 | return data.reduce(function(result,d){
5 | if(result.some(function(p){return p[1]===d[1];}))return result;
6 | result.push(d);
7 | return result;},
8 | []);}
--------------------------------------------------------------------------------
/src/constants.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | export const BLUE = '#4DC4E6';
3 | export const BLACK = '#333333';
4 | export const GREY = '#999999';
5 | export const RED = '#DF8165';
6 | export const WHITE = '#F5F5F5';
7 | export const YELLOW = 'rgba(255, 205, 0, 0.9)';
8 | export const GREEN = '#90C456';
9 | export const DARK_PURPLE = '#374E5C';
10 | export const LIGHT_PURPLE = '#4a697c';
11 |
--------------------------------------------------------------------------------
/dist/constants.js:
--------------------------------------------------------------------------------
1 | Object.defineProperty(exports,"__esModule",{value:true});
2 | var BLUE=exports.BLUE='#4DC4E6';
3 | var BLACK=exports.BLACK='#333333';
4 | var GREY=exports.GREY='#999999';
5 | var RED=exports.RED='#DF8165';
6 | var WHITE=exports.WHITE='#F5F5F5';
7 | var YELLOW=exports.YELLOW='rgba(255, 205, 0, 0.9)';
8 | var GREEN=exports.GREEN='#90C456';
9 | var DARK_PURPLE=exports.DARK_PURPLE='#374E5C';
10 | var LIGHT_PURPLE=exports.LIGHT_PURPLE='#4a697c';
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 |
8 | [*]
9 |
10 | # Change these settings to your own preference
11 | indent_style = tab
12 | indent_size = 2
13 |
14 | # We recommend you to keep these unchanged
15 | end_of_line = lf
16 | charset = utf-8
17 | trim_trailing_whitespace = true
18 | insert_final_newline = true
19 |
20 | [*.md]
21 | trim_trailing_whitespace = false
22 |
--------------------------------------------------------------------------------
/src/util.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | type Pair = [number, number];
3 | export function uniqueValuesInDataSet(data : Pair[]) : Pair[] {
4 | return data.reduce((result : Pair[], d : Pair) => {
5 | if (result.some(p => p[1] === d[1])) return result;
6 | result.push(d);
7 | return result;
8 | }, []);
9 | }
10 |
11 | export function uniqueValuesInDataSets(data : [Pair[]], index : number) : Pair[] {
12 | let values = [];
13 | data.forEach(Graph => {
14 | Graph.forEach(XYPair => {
15 | if (values.indexOf(XYPair[index]) === -1) {
16 | values.push(XYPair[index])
17 | }
18 | })
19 | });
20 | values.sort(function(a, b) {
21 | return a - b;
22 | });
23 | return values
24 | }
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 |
24 | # CocoaPods
25 | #
26 | # We recommend against adding the Pods directory to your .gitignore. However
27 | # you should judge for yourself, the pros and cons are mentioned at:
28 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
29 | #
30 | #Pods/
31 |
32 | /node_modules/
33 | .DS_Store
34 | lib
35 | project.xcworkspace
36 |
37 | # Android/IJ
38 | #
39 | .idea
40 | .gradle
41 | local.properties
42 |
43 | # node.js
44 | #
45 | node_modules/
46 | npm-debug.log
47 |
--------------------------------------------------------------------------------
/src/Circle.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import React, { Component, PropTypes } from 'react';
3 | import { ART } from 'react-native';
4 | const { Path, Shape } = ART;
5 | import * as C from './constants';
6 |
7 | export default class Circle extends Component {
8 | static propTypes = {
9 | radius: PropTypes.number.isRequired,
10 | x: PropTypes.number.isRequired,
11 | y: PropTypes.number.isRequired,
12 | onPress: PropTypes.func,
13 | fill: PropTypes.string,
14 | stroke: PropTypes.string,
15 | };
16 | static defaultProps = {
17 | onPress: () => {},
18 | radius: 2,
19 | fill: C.BLACK,
20 | stroke: C.BLACK,
21 | };
22 | render() {
23 | const { x, y, radius } = this.props;
24 | const path = new Path().moveTo(x, y - radius).arc(0, radius * 2, radius).arc(0, radius * -2, radius).close();
25 | return (
26 |
27 | );
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 onefold
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "globals": {},
4 | "env": {
5 | "node": true
6 | },
7 | "settings": {
8 | "ecmascript": 6,
9 | "jsx": true
10 | },
11 | "plugins": [
12 | "react",
13 | "react-native",
14 | "flowtype",
15 | "flow-vars"
16 | ],
17 | "extends": "airbnb",
18 | "rules": {
19 | "arrow-body-style": [0, "as-needed"],
20 | "camelcase": 0,
21 | "flow-vars/define-flow-type": 1,
22 | "flow-vars/use-flow-type": 1,
23 | "indent": [2, "tab"],
24 | "max-len": [2, 150, 4, {"ignoreComments": true, "ignoreUrls": true, "ignorePattern": "^\\s*var\\s.+=\\s*require\\s*\\("}],
25 | "no-alert": 0,
26 | "no-console": 1,
27 | "no-labels": 0,
28 | "no-param-reassign": [2, {"props": false}],
29 | "no-underscore-dangle": 0,
30 | "no-unused-vars": [1, {"args": "after-used", "argsIgnorePattern": "^_"}],
31 | "no-use-before-define": 0,
32 | "quote-props": 0,
33 | "quotes": 0,
34 | "react-native/no-unused-styles": 1,
35 | "react/jsx-indent": [2, "tab"],
36 | "react/jsx-indent-props": [2, "tab"],
37 | "react/jsx-no-bind": [1, { "allowArrowFunctions": true }],
38 | "react/no-multi-comp": 0,
39 | "react/prefer-stateless-function": 0,
40 | "space-infix-ops": 0,
41 | "strict": 0
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/react-native-chart.flow.js:
--------------------------------------------------------------------------------
1 | declare type Chart = {
2 | // Shared properties between most types
3 | data: Array>,
4 | type: 'line' | 'bar' | 'pie',
5 | highlightColor?: number | string,
6 | highlightIndices?: Array,
7 | onDataPointPress?: Function,
8 | axisColor?: number | string,
9 | axisLabelColor?: number | string,
10 | axisLineWidth?: number,
11 | gridColor?: number | string,
12 | gridLineWidth?: number,
13 | hideHorizontalGridLines?: boolean,
14 | hideVerticalGridLines?: boolean,
15 | showAxis?: boolean,
16 | showGrid?: boolean,
17 | showXAxisLabels?: boolean,
18 | showYAxisLabels?: boolean,
19 | style?: any,
20 | tightBounds?: boolean,
21 | verticalGridStep?: number,
22 | xAxisHeight?: number,
23 | yAxisTransform?: Function,
24 | yAxisWidth?: number,
25 | yAxisUseDecimal?: boolean,
26 |
27 | // Bar chart props
28 | color?: number | string,
29 | cornerRadius?: number,
30 | widthPercent?: number,
31 |
32 | // Line/multi-line chart props
33 | fillColor?: number | string,
34 | dataPointColor?: number | string,
35 | dataPointFillColor?: number | string,
36 | dataPointRadius?: number,
37 | lineWidth?: number,
38 | showDataPoint?: boolean,
39 |
40 | // Pie chart props
41 | sliceColors?: Array,
42 |
43 | // TODO
44 | highlightRadius?: number,
45 | pieCenterRatio?: number,
46 | animationDuration?: number,
47 | axisTitleColor?: number | string,
48 | axisTitleFontSize?: number,
49 | chartFontSize?: number,
50 | chartTitle?: string,
51 | chartTitleColor?: number | string,
52 | labelFontSize?: number,
53 | xAxisTitle?: string,
54 | yAxisTitle?: string,
55 | fillGradient?: Array,
56 | };
57 |
--------------------------------------------------------------------------------
/src/xAxis.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | 'use strict';
3 | import React, { Component, PropTypes } from 'react';
4 | import { View, Text, StyleSheet } from 'react-native';
5 | import { uniqueValuesInDataSets } from './util';
6 |
7 | const styles = StyleSheet.create({
8 | xAxisContainer: {
9 | flexDirection: 'row',
10 | flex: 0,
11 | backgroundColor: 'transparent',
12 | justifyContent: 'space-between',
13 | },
14 | axisText: {
15 | flex: 1,
16 | backgroundColor: 'transparent',
17 | },
18 | });
19 |
20 | export default class XAxis extends Component {
21 |
22 | static propTypes = {
23 | axisColor: PropTypes.any.isRequired,
24 | axisLabelColor: PropTypes.any.isRequired,
25 | axisLineWidth: PropTypes.number.isRequired,
26 | data: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.array)).isRequired,
27 | showXAxisLabels: PropTypes.bool.isRequired,
28 | style: PropTypes.any,
29 | width: PropTypes.number.isRequired,
30 | align: PropTypes.string,
31 | labelFontSize: PropTypes.number.isRequired,
32 | xAxisTransform: PropTypes.func,
33 | horizontalGridStep: PropTypes.number,
34 | };
35 | static defaultProps = {
36 | align: 'center',
37 | };
38 |
39 | render() {
40 | const data = uniqueValuesInDataSets(this.props.data || [[]], 0);
41 | let transform = (d) => d;
42 | if (this.props.xAxisTransform && typeof this.props.xAxisTransform === 'function') {
43 | transform = this.props.xAxisTransform;
44 | }
45 | return (
46 |
56 | {(() => {
57 | if (!this.props.showXAxisLabels) return null;
58 | return data.map((d, i) => {
59 | let stepsBetweenVerticalLines = this.props.horizontalGridStep ? Math.round((data.length) / this.props.horizontalGridStep + 1) : 1;
60 | if (stepsBetweenVerticalLines < 1) stepsBetweenVerticalLines = 1;
61 | if (i % stepsBetweenVerticalLines !== 0) return null;
62 | const item = transform(d);
63 | if (typeof item !== 'number' && !item) return null;
64 | return (
65 | {item}
76 | );
77 | });
78 |
79 | })()}
80 |
81 | );
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/BarChart.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import { View, StyleSheet, TouchableWithoutFeedback } from 'react-native';
3 | import React, { Component } from 'react';
4 | import * as C from './constants';
5 | import Grid from './Grid';
6 |
7 | const styles = StyleSheet.create({
8 | default: {
9 | flex: 1,
10 | alignItems: 'flex-end',
11 | flexDirection: 'row',
12 | justifyContent: 'space-around',
13 | },
14 | });
15 |
16 | export default class BarChart extends Component {
17 | constructor(props : any) {
18 | super(props);
19 | this.state = { };
20 | }
21 |
22 | _handlePress = (e : Object, dataPoint : number, index : number) => {
23 | if (this.props.data.onDataPointPress) {
24 | this.props.data.onDataPointPress(e, dataPoint, index);
25 | }
26 | };
27 |
28 | _drawBar = (_dataPoint : [number, number], index : number) => {
29 | const [_x, dataPoint] = _dataPoint;
30 | const backgroundColor = this.props.color[0] || C.BLUE;
31 | // the index [0] is facilitate multi-line, fix later if need be
32 | const HEIGHT = this.props.height;
33 | const WIDTH = this.props.width;
34 | let widthPercent = this.props.widthPercent || 0.5;
35 | if (widthPercent > 1) widthPercent = 1;
36 | if (widthPercent < 0) widthPercent = 0;
37 |
38 | let minBound = this.props.minVerticalBound;
39 | let maxBound = this.props.maxVerticalBound;
40 |
41 | // For all same values, create a range anyway
42 | if (minBound === maxBound) {
43 | minBound -= this.props.verticalGridStep;
44 | maxBound += this.props.verticalGridStep;
45 | }
46 |
47 | const data = this.props.data || [];
48 | const width = (WIDTH / data.length * this.props.horizontalScale * 0.5) * widthPercent;
49 | const divisor = (maxBound - minBound <= 0) ? 0.00001 : (maxBound - minBound);
50 | const scale = HEIGHT / divisor;
51 | let height = HEIGHT - ((minBound * scale) + (HEIGHT - (dataPoint * scale)));
52 | if (height <= 0) height = 20;
53 | return (
54 | this._handlePress(e, dataPoint, index)}
57 | >
58 |
67 |
68 | );
69 | };
70 |
71 | render() {
72 | const data = this.props.data || [];
73 | return (
74 |
75 |
76 | {data.map(this._drawBar)}
77 |
78 | );
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/dist/Circle.js:
--------------------------------------------------------------------------------
1 | Object.defineProperty(exports,"__esModule",{value:true});var _jsxFileName='src/Circle.js';var _createClass=function(){function defineProperties(target,props){for(var i=0;i, index : number) => colors[index] || colors[colors.length % index];
9 |
10 | export default class PieChart extends Component {
11 | constructor(props : any) {
12 | super(props);
13 | this.state = { rotation: 0 };
14 | (this:any).boundingAreas = {};
15 | }
16 | shouldComponentUpdate(props : any) {
17 | return (
18 | props.data !== this.props.data
19 | || props.height !== this.props.height
20 | || props.width !== this.props.width
21 | );
22 | }
23 |
24 | // TODO: Handle press on chart by emitting event
25 | _handlePress = (_e : Object) => {
26 | // const { locationX, locationY } = e.nativeEvent;
27 | };
28 |
29 | render() {
30 | if (!this.props.width || !this.props.height) return ;
31 |
32 | const COLORS = this.props.sliceColors || [
33 | C.BLUE,
34 | C.GREY,
35 | C.RED,
36 | C.YELLOW,
37 | C.GREEN,
38 | C.DARK_PURPLE,
39 | C.LIGHT_PURPLE,
40 | ];
41 |
42 | // TODO: Read stroke width from props?
43 | const STROKE_WIDTH = 1;
44 | const radius = (this.props.height / 2) - STROKE_WIDTH;
45 |
46 | const centerX = this.props.width / 2;
47 | const centerY = this.props.height / 2;
48 |
49 | // Gather sum of all data to determine angles
50 | let sum = 0;
51 | const data = this.props.data || [];
52 | data.forEach(n => { sum += (n[1] > 0) ? n[1] : 0.001; });
53 | const sectors = data.map(n => Math.floor(360 * (n[1]/sum)));
54 | let startAngle = 0;
55 |
56 | const arcs = [];
57 | const colors = [];
58 | sectors.forEach((sectionPiece, i) => {
59 | let endAngle = startAngle + sectionPiece;
60 | if (endAngle > 360) {
61 | endAngle = 360;
62 | }
63 | if (endAngle - startAngle === 0) {
64 | startAngle += sectionPiece;
65 | return;
66 | }
67 | if ((i === sectors.length - 1) && endAngle < 360) {
68 | endAngle = 360;
69 | }
70 | arcs.push({ startAngle, endAngle, outerRadius: radius });
71 | colors.push(getColor(COLORS, i));
72 | startAngle += sectionPiece;
73 | });
74 | return (
75 |
76 |
77 |
78 |
79 | {arcs.map((arc, i) => {
80 | return (
81 |
90 | );
91 | })}
92 |
93 |
94 |
95 |
96 | );
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/.flowconfig:
--------------------------------------------------------------------------------
1 | [ignore]
2 |
3 | # We fork some components by platform.
4 | .*/*.web.js
5 | .*/*.android.js
6 |
7 | # Some modules have their own node_modules with overlap
8 | .*/node_modules/node-haste/.*
9 |
10 | # Ugh
11 | .*/node_modules/babel.*
12 | .*/node_modules/babylon.*
13 | .*/node_modules/invariant.*
14 |
15 | # Ignore react and fbjs where there are overlaps, but don't ignore
16 | # anything that react-native relies on
17 | .*/node_modules/fbjs/lib/Map.js
18 | .*/node_modules/fbjs/lib/ErrorUtils.js
19 |
20 | # Flow has a built-in definition for the 'react' module which we prefer to use
21 | # over the currently-untyped source
22 | .*/node_modules/react/react.js
23 | .*/node_modules/react/lib/React.js
24 | .*/node_modules/react/lib/ReactDOM.js
25 |
26 | .*/__mocks__/.*
27 | .*/__tests__/.*
28 |
29 | .*/commoner/test/source/widget/share.js
30 |
31 | # Ignore commoner tests
32 | .*/node_modules/commoner/test/.*
33 |
34 | # See https://github.com/facebook/flow/issues/442
35 | .*/react-tools/node_modules/commoner/lib/reader.js
36 |
37 | # Ignore jest
38 | .*/node_modules/jest-cli/.*
39 |
40 | # Ignore Website
41 | .*/website/.*
42 |
43 | # Ignore generators
44 | .*/local-cli/generator.*
45 |
46 | # Ignore BUCK generated folders
47 | .*\.buckd/
48 |
49 | .*/node_modules/is-my-json-valid/test/.*\.json
50 | .*/node_modules/iconv-lite/encodings/tables/.*\.json
51 | .*/node_modules/y18n/test/.*\.json
52 | .*/node_modules/spdx-license-ids/spdx-license-ids.json
53 | .*/node_modules/spdx-exceptions/index.json
54 | .*/node_modules/resolve/test/subdirs/node_modules/a/b/c/x.json
55 | .*/node_modules/resolve/lib/core.json
56 | .*/node_modules/jsonparse/samplejson/.*\.json
57 | .*/node_modules/json5/test/.*\.json
58 | .*/node_modules/ua-parser-js/test/.*\.json
59 | .*/node_modules/builtin-modules/builtin-modules.json
60 | .*/node_modules/binary-extensions/binary-extensions.json
61 | .*/node_modules/url-regex/tlds.json
62 | .*/node_modules/joi/.*\.json
63 | .*/node_modules/isemail/.*\.json
64 | .*/node_modules/tr46/.*\.json
65 |
66 |
67 | [include]
68 | react-native-chart.flow.js
69 |
70 | [libs]
71 | node_modules/react-native/Libraries/react-native/react-native-interface.js
72 | node_modules/react-native/flow
73 | flow/
74 |
75 | [options]
76 | module.system=haste
77 |
78 | esproposal.class_static_fields=enable
79 | esproposal.class_instance_fields=enable
80 |
81 | munge_underscores=true
82 |
83 | module.name_mapper='^image![a-zA-Z0-9$_-]+$' -> 'GlobalImageStub'
84 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
85 |
86 | suppress_type=$FlowIssue
87 | suppress_type=$FlowFixMe
88 | suppress_type=$FixMe
89 |
90 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(2[0-4]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
91 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(2[0-4]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
92 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
93 |
94 | [version]
95 | 0.25.0
96 |
--------------------------------------------------------------------------------
/src/Grid.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import { View, StyleSheet } from 'react-native';
3 | import { uniqueValuesInDataSets } from './util';
4 |
5 | export default class Grid extends Component {
6 | static propTypes = {
7 | showGrid: PropTypes.bool,
8 | data: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.array)).isRequired,
9 | verticalGridStep: PropTypes.number.isRequired,
10 | horizontalGridStep: PropTypes.number,
11 | gridLineWidth: PropTypes.number,
12 | gridColor: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
13 | hideHorizontalGridLines: PropTypes.bool,
14 | hideVerticalGridLines: PropTypes.bool,
15 | height: PropTypes.number,
16 | width: PropTypes.number,
17 | type: PropTypes.oneOf(['line', 'bar', 'pie']).isRequired,
18 | };
19 | static defaultProps = {
20 |
21 | };
22 |
23 | render() {
24 | if (!this.props.showGrid) return null;
25 | const horizontalRange = [];
26 | const verticalRange = [];
27 | const xData = uniqueValuesInDataSets(this.props.data || [[]], 0);
28 | const yData = uniqueValuesInDataSets(this.props.data || [[]], 1);
29 | const horizontalSteps = (yData.length < this.props.verticalGridStep) ? yData.length : this.props.verticalGridStep;
30 | let stepsBetweenVerticalLines = this.props.horizontalGridStep ? Math.round(xData.length / this.props.horizontalGridStep) : 1;
31 | if (stepsBetweenVerticalLines < 1) stepsBetweenVerticalLines = 1;
32 |
33 | for (let i = horizontalSteps; i > 0; i--) horizontalRange.push(i);
34 | for (let i = xData.length - 1; i > 0; i -= stepsBetweenVerticalLines) verticalRange.push(i);
35 |
36 | const containerStyle = { width: this.props.width, height: this.props.height, position: 'absolute', left: 0 };
37 |
38 | let intendedLineWidth = this.props.gridLineWidth;
39 | if (this.props.gridLineWidth < 1) {
40 | intendedLineWidth = StyleSheet.hairlineWidth;
41 | }
42 |
43 | const horizontalGridStyle = {
44 | height: this.props.height / this.props.verticalGridStep,
45 | width: this.props.width,
46 | borderTopColor: this.props.gridColor,
47 | borderTopWidth: intendedLineWidth,
48 | };
49 |
50 | const verticalGridStyle = {
51 | height: this.props.height + 1,
52 | width: (this.props.width / (xData.length - 1)) * stepsBetweenVerticalLines,
53 | borderRightColor: this.props.gridColor,
54 | borderRightWidth: intendedLineWidth,
55 | };
56 |
57 | return (
58 |
59 | {(() => {
60 | if (this.props.hideHorizontalGridLines) return null;
61 | return (
62 |
63 | {horizontalRange.map((_, i) => )}
64 |
65 | );
66 | })()}
67 | {(() => {
68 | if (this.props.hideVerticalGridLines) return null;
69 | return (
70 |
71 | {verticalRange.map((_, i) => )}
72 |
73 | );
74 | })()}
75 |
76 | );
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "_args": [
3 | [
4 | {
5 | "name": "react-native-chart",
6 | "raw": "react-native-chart@1.0.8-beta",
7 | "rawSpec": "1.0.8-beta",
8 | "scope": null,
9 | "spec": "1.0.8-beta",
10 | "type": "version"
11 | },
12 | "/Users/aw2taman/Desktop/CompassApp"
13 | ]
14 | ],
15 | "_from": "react-native-chart@1.0.8-beta",
16 | "_id": "react-native-chart@1.0.8-beta",
17 | "_inCache": true,
18 | "_installable": true,
19 | "_location": "/react-native-chart",
20 | "_nodeVersion": "6.1.0",
21 | "_npmOperationalInternal": {
22 | "host": "packages-12-west.internal.npmjs.com",
23 | "tmp": "tmp/react-native-chart-1.0.8-beta.tgz_1469582943137_0.5609592183027416"
24 | },
25 | "_npmUser": {
26 | "email": "tom.auty@gmail.com",
27 | "name": "tomauty"
28 | },
29 | "_npmVersion": "3.9.0",
30 | "_phantomChildren": {},
31 | "_requested": {
32 | "name": "react-native-chart",
33 | "raw": "react-native-chart@1.0.8-beta",
34 | "rawSpec": "1.0.8-beta",
35 | "scope": null,
36 | "spec": "1.0.8-beta",
37 | "type": "version"
38 | },
39 | "_requiredBy": [
40 | "/"
41 | ],
42 | "_resolved": "https://registry.npmjs.org/react-native-chart/-/react-native-chart-1.0.8-beta.tgz",
43 | "_shasum": "c34b50ffe7f2acd6f519d614a134a7cd29135766",
44 | "_shrinkwrap": null,
45 | "_spec": "react-native-chart@1.0.8-beta",
46 | "_where": "/Users/aw2taman/Desktop/CompassApp",
47 | "dependencies": {
48 | "babel-cli": "^6.10.1",
49 | "babel-eslint": "^6.0.0",
50 | "babel-plugin-transform-flow-strip-types": "^6.8.0",
51 | "babel-preset-react-native": "^1.5.3"
52 | },
53 | "description": "[](https://gitter.im/tomauty/react-native-chart) [](https://badge.fury.",
54 | "devDependencies": {
55 | "eslint": "^2.8.0",
56 | "eslint-config-airbnb": "^8.0.0",
57 | "eslint-plugin-flow-vars": "^0.3.0",
58 | "eslint-plugin-flowtype": "^2.2.7",
59 | "eslint-plugin-import": "^1.6.0",
60 | "eslint-plugin-jsx-a11y": "^1.0.3",
61 | "eslint-plugin-react": "^5.0.1",
62 | "eslint-plugin-react-native": "^1.0.0"
63 | },
64 | "directories": {},
65 | "dist": {
66 | "shasum": "c34b50ffe7f2acd6f519d614a134a7cd29135766",
67 | "tarball": "https://registry.npmjs.org/react-native-chart/-/react-native-chart-1.0.8-beta.tgz"
68 | },
69 | "gitHead": "0ab98300945474df1224f5a4f78a754c88067de9",
70 | "main": "./src/Chart.js",
71 | "maintainers": [
72 | {
73 | "email": "hyunjincho@gmail.com",
74 | "name": "hyuncho"
75 | },
76 | {
77 | "email": "tom.auty@gmail.com",
78 | "name": "tomauty"
79 | }
80 | ],
81 | "name": "react-native-chart",
82 | "optionalDependencies": {},
83 | "readme": "ERROR: No README data found!",
84 | "scripts": {
85 | "build": "babel src --out-dir dist",
86 | "lint": "eslint src",
87 | "prepublish": "npm run build"
88 | },
89 | "version": "1.0.8-beta"
90 | }
91 |
--------------------------------------------------------------------------------
/src/yAxis.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import React, { Component, PropTypes } from 'react';
3 | import { View, Text, StyleSheet } from 'react-native';
4 | import { uniqueValuesInDataSets } from './util';
5 |
6 | const styles = StyleSheet.create({
7 | yAxisContainer: {
8 | flexDirection: 'column',
9 | justifyContent: 'space-between',
10 | flex: 1,
11 | paddingVertical: 0,
12 | paddingRight: 5,
13 | alignItems: 'flex-end',
14 | },
15 | });
16 |
17 |
18 | export default class YAxis extends Component {
19 |
20 | static propTypes = {
21 | axisColor: PropTypes.any,
22 | axisLineWidth: PropTypes.number,
23 | data: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.array)).isRequired,
24 | height: PropTypes.number.isRequired,
25 | placement: PropTypes.oneOf(['left', 'right']),
26 | verticalGridStep: PropTypes.number.isRequired,
27 | yAxisTransform: PropTypes.func,
28 | yAxisUseDecimal: PropTypes.bool,
29 | yAxisShortLabel: PropTypes.bool
30 | };
31 |
32 | static defaultProps : any = {
33 | placement: 'left',
34 | };
35 |
36 | constructor(props : any) {
37 | super(props);
38 | this.state = { bounds: { min: 0, max: 0 } };
39 | }
40 |
41 | // Credits: Martin Sznapka, StackOverflow, QuestionID: 9461621
42 | shortenLargeNumber(num, useDecimal) {
43 | let digits = (useDecimal) ? 2 : 0;
44 | var units = ['K', 'M', 'B', 't', 'P', 'E', 'Z', 'Y'],
45 | decimal;
46 | for (var i=units.length-1; i>=0; i--) {
47 | decimal = Math.pow(1000, i+1);
48 |
49 | if(num <= -decimal || num >= decimal) {
50 | return +(num / decimal).toFixed(digits) + units[i];
51 | }
52 | }
53 | return num;
54 | }
55 |
56 | _createLabelForYAxis = (index : number) => {
57 | let minBound = this.props.minVerticalBound;
58 | let maxBound = this.props.maxVerticalBound;
59 |
60 | // For all same values, create a range anyway
61 | if (minBound === maxBound) {
62 | minBound -= this.props.verticalGridStep;
63 | maxBound += this.props.verticalGridStep;
64 | }
65 | minBound = (minBound < 0) ? 0 : minBound;
66 | let label = minBound + (maxBound - minBound) / this.props.verticalGridStep * index;
67 | label = parseFloat(label.toFixed(3));
68 |
69 | if (!this.props.yAxisUseDecimal) {
70 | label = Math.round(label);
71 | }
72 |
73 | if (this.props.yAxisShortLabel) {
74 | label = this.shortenLargeNumber(label, this.props.yAxisUseDecimal);
75 | }
76 |
77 |
78 | if (this.props.yAxisTransform && typeof this.props.yAxisTransform === 'function') {
79 | label = this.props.yAxisTransform(label);
80 | }
81 | return (
82 |
89 | {label}
90 |
91 | );
92 | };
93 |
94 | render() {
95 | const range = [];
96 | const data = uniqueValuesInDataSets(this.props.data || [[]], 1);
97 | const steps = (data.length < this.props.verticalGridStep) ? data.length : this.props.verticalGridStep;
98 | for (let i = steps; i >= 0; i--) range.push(i);
99 | return (
100 |
108 | {range.map(this._createLabelForYAxis)}
109 |
110 | );
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/dist/xAxis.js:
--------------------------------------------------------------------------------
1 | 'use strict';Object.defineProperty(exports,"__esModule",{value:true});var _jsxFileName='src/xAxis.js';var _createClass=function(){function defineProperties(target,props){for(var i=0;i0;i--){horizontalRange.push(i);}
34 | for(var _i=data.length-1;_i>0;_i-=stepsBetweenVerticalLines){verticalRange.push(_i);}
35 |
36 | var containerStyle={width:this.props.width,height:this.props.height,position:'absolute',left:0};
37 |
38 | var intendedLineWidth=this.props.gridLineWidth;
39 | if(this.props.gridLineWidth<1){
40 | intendedLineWidth=_reactNative.StyleSheet.hairlineWidth;}
41 |
42 |
43 | var horizontalGridStyle={
44 | height:this.props.height/this.props.verticalGridStep,
45 | width:this.props.width,
46 | borderTopColor:this.props.gridColor,
47 | borderTopWidth:intendedLineWidth};
48 |
49 |
50 | var verticalGridStyle={
51 | height:this.props.height+1,
52 | width:this.props.width/data.length*stepsBetweenVerticalLines,
53 | borderRightColor:this.props.gridColor,
54 | borderRightWidth:intendedLineWidth};
55 |
56 |
57 | return (
58 | _react2.default.createElement(_reactNative.View,{style:containerStyle,__source:{fileName:_jsxFileName,lineNumber:58}},
59 | function(){
60 | if(_this2.props.hideHorizontalGridLines)return null;
61 | return (
62 | _react2.default.createElement(_reactNative.View,{style:{position:'absolute',flexDirection:'column',justifyContent:'space-around'},__source:{fileName:_jsxFileName,lineNumber:62}},
63 | horizontalRange.map(function(_,i){return _react2.default.createElement(_reactNative.View,{key:i,style:horizontalGridStyle,__source:{fileName:_jsxFileName,lineNumber:63}});})));}(),
64 |
65 |
66 |
67 | function(){
68 | if(_this2.props.hideVerticalGridLines)return null;
69 | return (
70 | _react2.default.createElement(_reactNative.View,{style:{flexDirection:'row',position:'absolute',justifyContent:'space-around'},__source:{fileName:_jsxFileName,lineNumber:70}},
71 | verticalRange.map(function(_,i){return _react2.default.createElement(_reactNative.View,{key:i,style:verticalGridStyle,__source:{fileName:_jsxFileName,lineNumber:71}});})));}()));}}]);return Grid;}(_react.Component);Grid.propTypes={showGrid:_react.PropTypes.bool,data:_react.PropTypes.arrayOf(_react.PropTypes.arrayOf(_react.PropTypes.array)).isRequired,verticalGridStep:_react.PropTypes.number.isRequired,horizontalGridStep:_react.PropTypes.number,gridLineWidth:_react.PropTypes.number,gridColor:_react.PropTypes.oneOfType([_react.PropTypes.number,_react.PropTypes.string]),hideHorizontalGridLines:_react.PropTypes.bool,hideVerticalGridLines:_react.PropTypes.bool,height:_react.PropTypes.number,width:_react.PropTypes.number,type:_react.PropTypes.oneOf(['line','bar','pie']).isRequired};Grid.defaultProps={};exports.default=Grid;
--------------------------------------------------------------------------------
/dist/yAxis.js:
--------------------------------------------------------------------------------
1 | Object.defineProperty(exports,"__esModule",{value:true});var _jsxFileName='src/yAxis.js';var _createClass=function(){function defineProperties(target,props){for(var i=0;i=0;i--){decimal=Math.pow(1000,i+1);if(num<=-decimal||num>=decimal){return +(num/decimal).toFixed(digits)+units[i];}}return num;}},{key:'render',value:function render()
91 |
92 |
93 |
94 | {
95 | var range=[];
96 | var data=this.props.data||[[]];
97 | var unique=(0,_util.uniqueValuesInDataSet)(data[0]);
98 | var steps=unique.length=0;i--){range.push(i);}
100 | return (
101 | _react2.default.createElement(_reactNative.View,{
102 | style:[
103 | styles.yAxisContainer,
104 | this.props.style||{},
105 | this.props.placement==='left'&&{borderRightColor:this.props.axisColor,borderRightWidth:this.props.axisLineWidth},
106 | this.props.placement==='right'&&{borderLeftColor:this.props.axisColor,borderLeftWidth:this.props.axisLineWidth}],__source:{fileName:_jsxFileName,lineNumber:101}},
107 |
108 |
109 | range.map(this._createLabelForYAxis)));}}]);return YAxis;}(_react.Component);YAxis.propTypes={axisColor:_react.PropTypes.any,axisLineWidth:_react.PropTypes.number,data:_react.PropTypes.arrayOf(_react.PropTypes.arrayOf(_react.PropTypes.array)).isRequired,height:_react.PropTypes.number.isRequired,placement:_react.PropTypes.oneOf(['left','right']),verticalGridStep:_react.PropTypes.number.isRequired,yAxisTransform:_react.PropTypes.func,yAxisUseDecimal:_react.PropTypes.bool,yAxisShortLabel:_react.PropTypes.bool};YAxis.defaultProps={placement:'left'};exports.default=YAxis;
--------------------------------------------------------------------------------
/dist/PieChart.js:
--------------------------------------------------------------------------------
1 | Object.defineProperty(exports,"__esModule",{value:true});var _extends=Object.assign||function(target){for(var i=1;i0?n[1]:0.001;});
53 | var sectors=data.map(function(n){return Math.floor(360*(n[1]/sum));});
54 | var startAngle=0;
55 |
56 | var arcs=[];
57 | var colors=[];
58 | sectors.forEach(function(sectionPiece,i){
59 | var endAngle=startAngle+sectionPiece;
60 | if(endAngle>360){
61 | endAngle=360;}
62 |
63 | if(endAngle-startAngle===0){
64 | startAngle+=sectionPiece;
65 | return;}
66 |
67 | if(i===sectors.length-1&&endAngle<360){
68 | endAngle=360;}
69 |
70 | arcs.push({startAngle:startAngle,endAngle:endAngle,outerRadius:radius});
71 | colors.push(getColor(COLORS,i));
72 | startAngle+=sectionPiece;});
73 |
74 | return (
75 | _react2.default.createElement(_reactNative.TouchableWithoutFeedback,{onPress:this._handlePress,__source:{fileName:_jsxFileName,lineNumber:75}},
76 | _react2.default.createElement(_reactNative.View,{__source:{fileName:_jsxFileName,lineNumber:76}},
77 | _react2.default.createElement(Surface,{width:this.props.width,height:this.props.height,__source:{fileName:_jsxFileName,lineNumber:77}},
78 | _react2.default.createElement(Group,{originX:centerX,width:this.props.width,height:this.props.height,originY:centerY,rotation:this.state.rotation,__source:{fileName:_jsxFileName,lineNumber:78}},
79 | arcs.map(function(arc,i){
80 | return (
81 | _react2.default.createElement(_Wedge2.default,_extends({
82 | stroke:colors[i],
83 | strokeWidth:STROKE_WIDTH,
84 | fill:colors[i],
85 | key:i,
86 | originX:centerX,
87 | originY:centerY},
88 | arc,{__source:{fileName:_jsxFileName,lineNumber:81}})));}))))));}}]);return PieChart;}(_react.Component);exports.default=PieChart;
--------------------------------------------------------------------------------
/dist/BarChart.js:
--------------------------------------------------------------------------------
1 | Object.defineProperty(exports,"__esModule",{value:true});var _extends=Object.assign||function(target){for(var i=1;i1)widthPercent=1;
36 | if(widthPercent<0)widthPercent=0;
37 |
38 | var minBound=_this.props.minVerticalBound;
39 | var maxBound=_this.props.maxVerticalBound;
40 |
41 | // For all same values, create a range anyway
42 | if(minBound===maxBound){
43 | minBound-=_this.props.verticalGridStep;
44 | maxBound+=_this.props.verticalGridStep;}
45 |
46 |
47 | var data=_this.props.data||[];
48 | var width=WIDTH/data.length*_this.props.horizontalScale*0.5*widthPercent;
49 | var divisor=maxBound-minBound<=0?0.00001:maxBound-minBound;
50 | var scale=HEIGHT/divisor;
51 | var height=HEIGHT-(minBound*scale+(HEIGHT-dataPoint*scale));
52 | if(height<=0)height=20;
53 | return (
54 | _react2.default.createElement(_reactNative.TouchableWithoutFeedback,{
55 | key:index,
56 | onPress:function onPress(e){return _this._handlePress(e,dataPoint,index);},__source:{fileName:_jsxFileName,lineNumber:54}},
57 |
58 | _react2.default.createElement(_reactNative.View,{
59 | style:{
60 | borderTopLeftRadius:_this.props.cornerRadius||0,
61 | borderTopRightRadius:_this.props.cornerRadius||0,
62 | backgroundColor:backgroundColor,
63 | width:width,
64 | height:height},__source:{fileName:_jsxFileName,lineNumber:58}})));};_this.state={};return _this;}_createClass(BarChart,[{key:'render',value:function render()
65 |
66 |
67 |
68 |
69 |
70 |
71 | {
72 | var data=this.props.data||[];
73 | return (
74 | _react2.default.createElement(_reactNative.View,{ref:'container',style:[styles.default],__source:{fileName:_jsxFileName,lineNumber:74}},
75 | _react2.default.createElement(_Grid2.default,_extends({},this.props,{__source:{fileName:_jsxFileName,lineNumber:75}})),
76 | data.map(this._drawBar)));}}]);return BarChart;}(_react.Component);exports.default=BarChart;
--------------------------------------------------------------------------------
/src/Wedge.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Taken from react-art, changed for RN.
3 | * Copyright 2013-2014 Facebook, Inc.
4 | * All rights reserved.
5 | *
6 | * This source code is licensed under the BSD-style license found in the
7 | * LICENSE file in the root directory of this source tree. An additional grant
8 | * of patent rights can be found in the PATENTS file in the same directory.
9 | *
10 | * @providesModule Wedge.art
11 | * @typechecks
12 | *
13 | * Example usage:
14 | *
20 | *
21 | * Additional optional property:
22 | * (Int) innerRadius
23 | *
24 | */
25 |
26 | import React, { Component, PropTypes } from 'react';
27 | import { ART } from 'react-native';
28 | const { Shape, Path } = ART;
29 |
30 | /**
31 | * Wedge is a React component for drawing circles, wedges and arcs. Like other
32 | * ReactART components, it must be used in a .
33 | */
34 | export default class Wedge extends Component {
35 |
36 | static propTypes = {
37 | outerRadius: PropTypes.number.isRequired,
38 | startAngle: PropTypes.number.isRequired,
39 | endAngle: PropTypes.number.isRequired,
40 | originX: PropTypes.number.isRequired,
41 | originY: PropTypes.number.isRequired,
42 | innerRadius: PropTypes.number,
43 | };
44 |
45 |
46 | constructor(props : any) {
47 | super(props);
48 | (this:any).circleRadians = Math.PI * 2;
49 | (this:any).radiansPerDegree = Math.PI / 180;
50 | (this:any)._degreesToRadians = this._degreesToRadians.bind(this);
51 | }
52 |
53 | /**
54 | * _degreesToRadians(degrees)
55 | *
56 | * Helper function to convert degrees to radians
57 | *
58 | * @param {number} degrees
59 | * @return {number}
60 | */
61 | _degreesToRadians(degrees : number) : number {
62 | if (degrees !== 0 && degrees % 360 === 0) { // 360, 720, etc.
63 | return (this:any).circleRadians;
64 | }
65 | return degrees * (this:any).radiansPerDegree % (this:any).circleRadians;
66 | }
67 |
68 | /**
69 | * _createCirclePath(or, ir)
70 | *
71 | * Creates the ReactART Path for a complete circle.
72 | *
73 | * @param {number} or The outer radius of the circle
74 | * @param {number} ir The inner radius, greater than zero for a ring
75 | * @return {object}
76 | */
77 | _createCirclePath(or : number, ir : number) : Path {
78 | const path = new Path();
79 |
80 | path.move(0, or)
81 | .arc(or * 2, 0, or)
82 | .arc(-or * 2, 0, or);
83 |
84 | if (ir) {
85 | path.move(or - ir, 0)
86 | .counterArc(ir * 2, 0, ir)
87 | .counterArc(-ir * 2, 0, ir);
88 | }
89 |
90 | path.close();
91 |
92 | return path;
93 | }
94 |
95 | /**
96 | * _createArcPath(sa, ea, ca, or, ir)
97 | *
98 | * Creates the ReactART Path for an arc or wedge.
99 | *
100 | * @param {number} startAngle The starting degrees relative to 12 o'clock
101 | * @param {number} endAngle The ending degrees relative to 12 o'clock
102 | * @param {number} or The outer radius in pixels
103 | * @param {number} ir The inner radius in pixels, greater than zero for an arc
104 | * @return {object}
105 | */
106 | _createArcPath(originX : number, originY : number, startAngle : number, endAngle : number, or : number, ir : number) : Path {
107 | const path = new Path();
108 |
109 | // angles in radians
110 | const sa = this._degreesToRadians(startAngle);
111 | const ea = this._degreesToRadians(endAngle);
112 |
113 | // central arc angle in radians
114 | const ca = sa > ea ? (this:any).circleRadians - sa + ea : ea - sa;
115 |
116 | // cached sine and cosine values
117 | const ss = Math.sin(sa);
118 | const es = Math.sin(ea);
119 | const sc = Math.cos(sa);
120 | const ec = Math.cos(ea);
121 |
122 | // cached differences
123 | const ds = es - ss;
124 | const dc = ec - sc;
125 | const dr = ir - or;
126 |
127 | // if the angle is over pi radians (180 degrees)
128 | // we will need to let the drawing method know.
129 | const large = ca > Math.PI;
130 |
131 | // TODO (sema) Please improve theses comments to make the math
132 | // more understandable.
133 | //
134 | // Formula for a point on a circle at a specific angle with a center
135 | // at (0, 0):
136 | // x = radius * Math.sin(radians)
137 | // y = radius * Math.cos(radians)
138 | //
139 | // For our starting point, we offset the formula using the outer
140 | // radius because our origin is at (top, left).
141 | // In typical web layout fashion, we are drawing in quadrant IV
142 | // (a.k.a. Southeast) where x is positive and y is negative.
143 | //
144 | // The arguments for path.arc and path.counterArc used below are:
145 | // (endX, endY, radiusX, radiusY, largeAngle)
146 |
147 | path.move(or + or * ss, or - or * sc) // move to starting point
148 | .arc(or * ds, or * -dc, or, or, large) // outer arc
149 | .line(dr * es, dr * -ec); // width of arc or wedge
150 |
151 | if (ir) {
152 | path.counterArc(ir * -ds, ir * dc, ir, ir, large); // inner arc
153 | }
154 |
155 | return path;
156 | }
157 |
158 | render() : any {
159 | // angles are provided in degrees
160 | const startAngle = this.props.startAngle;
161 | const endAngle = this.props.endAngle;
162 | // if (startAngle - endAngle === 0) {
163 | // return null;
164 | // }
165 |
166 | // radii are provided in pixels
167 | const innerRadius = this.props.innerRadius || 0;
168 | const outerRadius = this.props.outerRadius;
169 |
170 | const { originX, originY } = this.props;
171 |
172 | // sorted radii
173 | const ir = Math.min(innerRadius, outerRadius);
174 | const or = Math.max(innerRadius, outerRadius);
175 |
176 | let path;
177 | if (endAngle >= startAngle + 360) {
178 | path = this._createCirclePath(or, ir);
179 | } else {
180 | path = this._createArcPath(originX, originY, startAngle, endAngle, or, ir);
181 | }
182 |
183 | return ;
184 | }
185 | }
186 |
--------------------------------------------------------------------------------
/src/LineChart.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import React, { Component } from 'react';
3 | import { Animated, ART, View, Platform, TouchableOpacity } from 'react-native';
4 | const { Surface, Shape, Path } = ART;
5 | import * as C from './constants';
6 | import Circle from './Circle';
7 | const AnimatedShape = Animated.createAnimatedComponent(Shape);
8 | import Grid from './Grid';
9 | import { uniqueValuesInDataSets } from './util';
10 |
11 | const makeDataPoint = (x : number, y : number, data : any, index : number) => {
12 |
13 | let color = (data.color[index]) ? data.color[index] : C.BLUE;
14 |
15 | let fill = ((data.dataPointFillColor) && (data.dataPointFillColor[index])) ?
16 | (data.dataPointFillColor[index]) : color;
17 |
18 | let stroke = ((data.dataPointColor) && (data.dataPointColor[index])) ?
19 | (data.dataPointColor[index]) : color;
20 |
21 | return {
22 | x,
23 | y,
24 | radius: data.dataPointRadius,
25 | fill: fill,
26 | stroke: stroke };
27 | };
28 |
29 | const calculateDivisor = (minBound : number, maxBound : number) : number => {
30 | return (maxBound - minBound <= 0) ? 0.00001 : maxBound - minBound;
31 | };
32 |
33 | const heightZero = (Platform.OS === 'ios') ? 0 : 1;
34 |
35 | export default class LineChart extends Component {
36 |
37 | constructor(props : any) {
38 | super(props);
39 | const heightValue = (props.animated) ? heightZero : props.height;
40 | const opacityValue = (props.animated) ? 0 : 1;
41 | this.state = { height: new Animated.Value(heightValue), opacity: new Animated.Value(opacityValue) };
42 | }
43 |
44 | componentWillUpdate() {
45 | if (this.props.animated) {
46 | Animated.timing(this.state.opacity, { duration: 0, toValue: 0 }).start();
47 | Animated.timing(this.state.height, { duration: 0, toValue: heightZero }).start();
48 | }
49 | }
50 |
51 | componentDidUpdate() {
52 | if (this.props.animated) {
53 | Animated.timing(this.state.height, { duration: this.props.animationDuration, toValue: this.props.height }).start();
54 | Animated.timing(this.state.opacity, { duration: this.props.animationDuration, toValue: 1 }).start();
55 | }
56 | }
57 |
58 | _drawLine = () => {
59 | const containerHeight = this.props.height;
60 | const containerWidth = this.props.width;
61 | const data = this.props.data || [[]];
62 | let minBound = this.props.minVerticalBound;
63 | let maxBound = this.props.maxVerticalBound;
64 |
65 | // For all same values, create a range anyway
66 | if (minBound === maxBound) {
67 | minBound -= this.props.verticalGridStep;
68 | maxBound += this.props.verticalGridStep;
69 | }
70 |
71 | const divisor = calculateDivisor(minBound, maxBound);
72 | const scale = (containerHeight + 1) / divisor;
73 | const horizontalStep = containerWidth / uniqueValuesInDataSets(data, 0).length;
74 |
75 | const dataPoints = [];
76 | const path = [];
77 | const fillPath = [];
78 |
79 | for (index = 0; index < data.length; index++) {
80 | var pathArray = [], fillPathArray = [], pathSubIndex = -1;
81 | let currentData = data[index] || [];
82 | const firstDataPoint = currentData[0][1];
83 | let height = (minBound * scale) + (containerHeight - (firstDataPoint * scale));
84 | if (height < 0) height = 0;
85 |
86 | const dataPointSet = [];
87 | dataPointSet.push(makeDataPoint(0, height, this.props, index));
88 |
89 | let beginNewPath = true;
90 | currentData.forEach(([_, dataPoint], i) => {
91 |
92 | if (dataPoint === '') {
93 | // An empty within the graph, begin new Path next non-empty datapoint
94 | // beginNewPath = true;
95 | return;
96 | }
97 |
98 | let _height = (minBound * scale) + (containerHeight - (dataPoint * scale));
99 | if (_height < 0) _height = 0;
100 |
101 | const x = horizontalStep * (i);
102 | const y = Math.round(_height);
103 |
104 | dataPointSet.push(makeDataPoint(x, y, this.props, index));
105 |
106 | if ((beginNewPath) && (dataPoint !== '')) {
107 | pathArray.push(new Path().moveTo(x, y));
108 | fillPathArray.push(new Path().moveTo(x, containerHeight).lineTo(x, height));
109 | pathSubIndex++;
110 | beginNewPath = false;
111 | } else {
112 | pathArray[pathSubIndex].lineTo(x, y);
113 | fillPathArray[pathSubIndex].lineTo(x, y);
114 | }
115 | });
116 |
117 | dataPoints.push(dataPointSet);
118 |
119 | for (g = 0; g < pathArray.length; g++) {
120 | fillPathArray[g].lineTo(dataPointSet[dataPointSet.length - 1].x, containerHeight);
121 | if (this.props.fillColor) {
122 | fillPathArray[g].moveTo(0, containerHeight);
123 | }
124 |
125 | if (pathArray[g].path.some(isNaN)) return null;
126 | }
127 |
128 | path.push(pathArray);
129 | fillPath.push(fillPathArray);
130 | }
131 |
132 | var multipleLines = dataPoints.map( (dataPointSet, index) => {
133 | let color = (this.props.color[index]) ? this.props.color[index] : C.BLUE;
134 | let allDisjointPaths = path[index].map( (singlePath) => {
135 | return (
136 |
137 | );
138 | });
139 | return allDisjointPaths;
140 | });
141 |
142 | var multipleFills = dataPoints.map( (dataPointSet, index) => {
143 | let allDisjointPaths = fillPath[index].map ( (singlePath, subIndex) => {
144 | return (
145 |
146 | );
147 | });
148 | return allDisjointPaths;
149 | });
150 |
151 | return (
152 |
153 |
154 |
155 | { multipleLines }
156 | { multipleFills }
157 |
158 |
159 |
160 |
161 |
162 | {(() => {
163 | if (!this.props.showDataPoint) return null;
164 |
165 | var multipleDataPoints = dataPoints.map( (dataPointSet, index) => {
166 | let totalDataSet = dataPointSet.map((d, i) => {
167 | return (
168 | alert(i)} />
169 | );
170 | });
171 | return totalDataSet;
172 | });
173 |
174 | return (
175 |
176 | { multipleDataPoints }
177 |
178 | );
179 | })()}
180 |
181 | );
182 | };
183 |
184 | render() : any {
185 | if (Platform.OS === 'ios') {
186 | return (
187 |
188 |
189 |
190 | {this._drawLine()}
191 |
192 |
193 | );
194 | }
195 | return (
196 |
197 |
198 |
199 | {this._drawLine()}
200 |
201 |
202 | );
203 | }
204 | }
205 |
--------------------------------------------------------------------------------
/dist/Wedge.js:
--------------------------------------------------------------------------------
1 | Object.defineProperty(exports,"__esModule",{value:true});var _extends=Object.assign||function(target){for(var i=1;i
46 | *
47 | * Additional optional property:
48 | * (Int) innerRadius
49 | *
50 | */var Shape=_reactNative.ART.Shape;var Path=_reactNative.ART.Path; /**
51 | * Wedge is a React component for drawing circles, wedges and arcs. Like other
52 | * ReactART components, it must be used in a .
53 | */var Wedge=function(_Component){_inherits(Wedge,_Component);function Wedge(props){_classCallCheck(this,Wedge);var _this=_possibleConstructorReturn(this,Object.getPrototypeOf(Wedge).call(this,props));_this.circleRadians=Math.PI*2;_this.radiansPerDegree=Math.PI/180;_this._degreesToRadians=_this._degreesToRadians.bind(_this);return _this;} /**
54 | * _degreesToRadians(degrees)
55 | *
56 | * Helper function to convert degrees to radians
57 | *
58 | * @param {number} degrees
59 | * @return {number}
60 | */_createClass(Wedge,[{key:'_degreesToRadians',value:function _degreesToRadians(
61 | degrees){
62 | if(degrees!==0&°rees%360===0){ // 360, 720, etc.
63 | return this.circleRadians;}
64 |
65 | return degrees*this.radiansPerDegree%this.circleRadians;}
66 |
67 |
68 | /**
69 | * _createCirclePath(or, ir)
70 | *
71 | * Creates the ReactART Path for a complete circle.
72 | *
73 | * @param {number} or The outer radius of the circle
74 | * @param {number} ir The inner radius, greater than zero for a ring
75 | * @return {object}
76 | */},{key:'_createCirclePath',value:function _createCirclePath(
77 | or,ir){
78 | var path=new Path();
79 |
80 | path.move(0,or).
81 | arc(or*2,0,or).
82 | arc(-or*2,0,or);
83 |
84 | if(ir){
85 | path.move(or-ir,0).
86 | counterArc(ir*2,0,ir).
87 | counterArc(-ir*2,0,ir);}
88 |
89 |
90 | path.close();
91 |
92 | return path;}
93 |
94 |
95 | /**
96 | * _createArcPath(sa, ea, ca, or, ir)
97 | *
98 | * Creates the ReactART Path for an arc or wedge.
99 | *
100 | * @param {number} startAngle The starting degrees relative to 12 o'clock
101 | * @param {number} endAngle The ending degrees relative to 12 o'clock
102 | * @param {number} or The outer radius in pixels
103 | * @param {number} ir The inner radius in pixels, greater than zero for an arc
104 | * @return {object}
105 | */},{key:'_createArcPath',value:function _createArcPath(
106 | originX,originY,startAngle,endAngle,or,ir){
107 | var path=new Path();
108 |
109 | // angles in radians
110 | var sa=this._degreesToRadians(startAngle);
111 | var ea=this._degreesToRadians(endAngle);
112 |
113 | // central arc angle in radians
114 | var ca=sa>ea?this.circleRadians-sa+ea:ea-sa;
115 |
116 | // cached sine and cosine values
117 | var ss=Math.sin(sa);
118 | var es=Math.sin(ea);
119 | var sc=Math.cos(sa);
120 | var ec=Math.cos(ea);
121 |
122 | // cached differences
123 | var ds=es-ss;
124 | var dc=ec-sc;
125 | var dr=ir-or;
126 |
127 | // if the angle is over pi radians (180 degrees)
128 | // we will need to let the drawing method know.
129 | var large=ca>Math.PI;
130 |
131 | // TODO (sema) Please improve theses comments to make the math
132 | // more understandable.
133 | //
134 | // Formula for a point on a circle at a specific angle with a center
135 | // at (0, 0):
136 | // x = radius * Math.sin(radians)
137 | // y = radius * Math.cos(radians)
138 | //
139 | // For our starting point, we offset the formula using the outer
140 | // radius because our origin is at (top, left).
141 | // In typical web layout fashion, we are drawing in quadrant IV
142 | // (a.k.a. Southeast) where x is positive and y is negative.
143 | //
144 | // The arguments for path.arc and path.counterArc used below are:
145 | // (endX, endY, radiusX, radiusY, largeAngle)
146 |
147 | path.move(or+or*ss,or-or*sc) // move to starting point
148 | .arc(or*ds,or*-dc,or,or,large) // outer arc
149 | .line(dr*es,dr*-ec); // width of arc or wedge
150 |
151 | if(ir){
152 | path.counterArc(ir*-ds,ir*dc,ir,ir,large); // inner arc
153 | }
154 |
155 | return path;}},{key:'render',value:function render()
156 |
157 |
158 | {
159 | // angles are provided in degrees
160 | var startAngle=this.props.startAngle;
161 | var endAngle=this.props.endAngle;
162 | // if (startAngle - endAngle === 0) {
163 | // return null;
164 | // }
165 |
166 | // radii are provided in pixels
167 | var innerRadius=this.props.innerRadius||0;
168 | var outerRadius=this.props.outerRadius;var _props=
169 |
170 | this.props;var originX=_props.originX;var originY=_props.originY;
171 |
172 | // sorted radii
173 | var ir=Math.min(innerRadius,outerRadius);
174 | var or=Math.max(innerRadius,outerRadius);
175 |
176 | var path=void 0;
177 | if(endAngle>=startAngle+360){
178 | path=this._createCirclePath(or,ir);}else
179 | {
180 | path=this._createArcPath(originX,originY,startAngle,endAngle,or,ir);}
181 |
182 |
183 | return _react2.default.createElement(Shape,_extends({},this.props,{d:path,__source:{fileName:_jsxFileName,lineNumber:183}}));}}]);return Wedge;}(_react.Component);Wedge.propTypes={outerRadius:_react.PropTypes.number.isRequired,startAngle:_react.PropTypes.number.isRequired,endAngle:_react.PropTypes.number.isRequired,originX:_react.PropTypes.number.isRequired,originY:_react.PropTypes.number.isRequired,innerRadius:_react.PropTypes.number};exports.default=Wedge;
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-native-chart
2 |
3 | ### NOTE: I have not been able to maintain this repo. Recommend switching to [Victory Charts](https://github.com/FormidableLabs/victory-native).
4 |
5 | 
6 |
7 | ## Getting Started
8 | [](https://nodei.co/npm/react-native-chart/)
9 |
10 | 1. `npm i react-native-chart --save`
11 |
12 | __Link ART to your project__
13 |
14 | 1. Right click Libraries and click 'Add Files to {YourProject}'
15 |
16 |
17 |
18 | 2. Navigate to your project's node_modules/react-native/Libraries/ART and select 'ART.xcodeproj'
19 |
20 |
21 |
22 | 3. Go to Build Phases -> Link Binary With Libraries
23 |
24 |
25 |
26 | 4 Click the '+', and add libART.a
27 |
28 |
29 |
30 | Then rebuild.
31 |
32 |
33 |
34 | ## Usage
35 | ```javascript
36 | import React, { StyleSheet, View, Component } from 'react-native';
37 | import Chart from 'react-native-chart';
38 |
39 | const styles = StyleSheet.create({
40 | container: {
41 | flex: 1,
42 | justifyContent: 'center',
43 | alignItems: 'center',
44 | backgroundColor: 'white',
45 | },
46 | chart: {
47 | width: 200,
48 | height: 200,
49 | },
50 | });
51 |
52 | const data = [[
53 | [0, 1],
54 | [1, 3],
55 | [3, 7],
56 | [4, 9],
57 | ]];
58 |
59 | class SimpleChart extends Component {
60 | render() {
61 | return (
62 |
63 |
71 |
72 | );
73 | }
74 | }
75 |
76 | ```
77 | ## Properties
78 |
79 | Use '' y-values to signify the 'render but empty' data points.
80 |
81 | | Property | Type | Description | Required | Default |
82 | | ----------------------- | ------------------------- | --------------------------------------------------------- | -------- | --------------------- |
83 | | data | Array< Array< [number, number] > > | An array of arrays of [x, y] pairs. | **Yes** | |
84 | | type | string | pie/bar/line | **Yes** | bar |
85 | | color | Array < string > | Color of bars/line in line chart | No | #4DC4E6 |
86 | | cornerRadius | number | Corner radius of bars in bar chart | No | 0 |
87 | | fillColor | Array < string > | Fill area colors in line chart | No | |
88 | | dataPointColor | Array < string > | Stroke colors for line chart data point | No | |
89 | | dataPointFillColor | Array < string > | Fill colors for line chart data point | No | |
90 | | dataPointRadius | number | Radius of the data point | No | 3 |
91 | | lineWidth | number | Width of line chart line | No | 1 |
92 | | showDataPoint | boolean | Show data points on line chart | No | false |
93 | | sliceColors | Array < string > | Array of colors for pie chart slices | **Yes** | [ < random colors > ] |
94 | | axisColor | string | Color of axis lines | No | #333333 |
95 | | axisLabelColor | string | Color of axis test | No | #333333 |
96 | | axisLineWidth | number | Width of axis lines | No | 1 |
97 | | gridColor | string | Color of grid lines | No | #333333 |
98 | | gridLineWidth | number | Width of grid lines | No | 0.5 |
99 | | hideHorizontalGridLines | boolean | Hide grid lines going from LTR | No | false |
100 | | hideVerticalGridLines | boolean | Hide grid lines going up -> down | No | false |
101 | | showAxis | boolean | Show the X and Y axes | No | true |
102 | | showGrid | boolean | Show the grid | No | true |
103 | | showXAxisLabels | boolean | Show X-Axis labels | No | true |
104 | | showYAxisLabels | boolean | Show Y-Axis labels | No | true |
105 | | style | object | Style on the container | No | {} |
106 | | tightBounds | boolean | Tighten min and max bounds strictly to min/max in dataset | No | false |
107 | | verticalGridStep | number | How many vertical grid lines to show | No | 4 |
108 | | horizontalGridStep | number | How many horizontal grid lines to show | No | all |
109 | | xAxisHeight | number | Height of X-axis container | No | 20 |
110 | | yAxisTransform | Function | Transform data point to y-axis label | No | (_) => _ |
111 | | xAxisTransform | Function | Transform data point to x-axis label | No | (_) => _ |
112 | | yAxisWidth | number | Width of the Y-axis container | No | 30 |
113 | | yAxisUseDecimal | boolean | Show decimals on Y-axis labels | No | false |
114 | | yAxisShortLabel | boolean | Shorten yAxis labels with K, M, B for thousand<->billion, etc | No | false |
115 |
116 | ## TODO
117 | - [ ] Code cleanup
118 | - [X] Multi-line chart
119 | - [ ] Horizontal line chart
120 | - [ ] Scatter chart
121 |
122 |
--------------------------------------------------------------------------------
/src/Chart.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | 'use strict';
3 | import React, { Component, PropTypes } from 'react';
4 | import { LayoutAnimation, StyleSheet, View } from 'react-native';
5 | import BarChart from './BarChart';
6 | import LineChart from './LineChart';
7 | import PieChart from './PieChart';
8 | import YAxis from './yAxis';
9 | import XAxis from './xAxis';
10 | import * as C from './constants';
11 |
12 | const styles = StyleSheet.create({
13 | default: {},
14 | });
15 |
16 | const getRoundNumber = (value, gridStep) => {
17 | if (value <= 0) return 0;
18 | const logValue = Math.log10(value);
19 | const scale = Math.pow(10, Math.floor(logValue));
20 | const n = Math.ceil(value / scale * 4);
21 |
22 | let tmp = n % gridStep;
23 | if (tmp !== 0) tmp += (gridStep - tmp);
24 | return n * scale / 4.0;
25 | };
26 |
27 |
28 | export default class Chart extends Component {
29 | static defaultProps : any = {
30 | data: [[]],
31 | animated: true,
32 | animationDuration: 300,
33 | axisColor: C.BLACK,
34 | axisLabelColor: C.BLACK,
35 | axisLineWidth: 1,
36 | axisTitleColor: C.GREY,
37 | axisTitleFontSize: 16,
38 | chartFontSize: 14,
39 | color: [],
40 | dataPointRadius: 3,
41 | gridColor: C.BLACK,
42 | gridLineWidth: 0.5,
43 | hideHorizontalGridLines: false,
44 | hideVerticalGridLines: false,
45 | horizontalScale: 1,
46 | labelFontSize: 10,
47 | lineWidth: 1,
48 | showAxis: true,
49 | showDataPoint: false,
50 | showGrid: true,
51 | showXAxisLabels: true,
52 | showYAxisLabels: true,
53 | tightBounds: false,
54 | verticalGridStep: 4,
55 | xAxisHeight: 20,
56 | yAxisWidth: 30,
57 | yAxisUseDecimal: false,
58 | yAxisShortLabel: false,
59 | };
60 |
61 | constructor(props : any) {
62 | super(props);
63 | this.state = { bounds: { min: 0, max: 0 } };
64 | }
65 | componentDidMount() {
66 | this._computeBounds();
67 | }
68 |
69 | shouldComponentUpdate(props, state) {
70 | return props !== this.props || state !== this.state;
71 | }
72 |
73 | componentDidUpdate(props : any) {
74 | if (this.props !== props) {
75 | this._computeBounds();
76 | }
77 | }
78 |
79 | _computeBounds() : any {
80 | let min = Infinity;
81 | let max = -Infinity;
82 | const data = this.props.data || [[]];
83 |
84 | data.forEach(Graph => {
85 | Graph.forEach(XYPair => {
86 | const number = XYPair[1];
87 | // Addition for blank spaces in graphs - use '' as y-coord
88 | if (number === '') {
89 | return;
90 | }
91 |
92 | if (number < min) min = number;
93 | if (number > max) max = number;
94 | });
95 | });
96 |
97 | let ceilMax = Math.ceil(max);
98 | let floorMin = Math.floor(min);
99 |
100 | if ((ceilMax - floorMin) > this.props.verticalGridStep) {
101 | min = floorMin;
102 | max = ceilMax;
103 | }
104 |
105 | // Exit if we want tight bounds
106 | if (this.props.tightBounds) {
107 | return this.setState({ bounds: { min, max } });
108 | }
109 |
110 | max = getRoundNumber(max, this.props.verticalGridStep);
111 | if (min < 0) {
112 | let step;
113 |
114 | if (this.props.verticalGridStep > 3) {
115 | step = Math.abs(max - min) / (this.props.verticalGridStep - 1);
116 | } else {
117 | step = Math.max(Math.abs(max - min) / 2, Math.max(Math.abs(min), Math.abs(max)));
118 | }
119 | step = getRoundNumber(step, this.props.verticalGridStep);
120 | let newMin;
121 | let newMax;
122 |
123 | if (Math.abs(min) > Math.abs(max)) {
124 | const m = Math.ceil(Math.abs(min) / step);
125 | newMin = step * m * (min > 0 ? 1 : -1);
126 | newMax = step * (this.props.verticalGridStep - m) * (max > 0 ? 1 : -1);
127 | } else {
128 | const m = Math.ceil(Math.abs(max) / step);
129 | newMax = step * m * (max > 0 ? 1 : -1);
130 | newMin = step * (this.props.verticalGridStep - m) * (min > 0 ? 1 : -1);
131 | }
132 | if (min < newMin) {
133 | newMin -= step;
134 | newMax -= step;
135 | }
136 | if (max > newMax + step) {
137 | newMin += step;
138 | newMax += step;
139 | }
140 | if (max < min) {
141 | const tmp = max;
142 | max = min;
143 | min = tmp;
144 | }
145 | }
146 | return this.setState({ bounds: { max, min } });
147 | }
148 |
149 | _onContainerLayout = (e : Object) => this.setState({
150 | containerHeight: e.nativeEvent.layout.height,
151 | containerWidth: e.nativeEvent.layout.width,
152 | });
153 |
154 | _minVerticalBound() : number {
155 | if (this.props.tightBounds) return this.state.bounds.min;
156 | return (this.state.bounds.min > 0) ? this.state.bounds.min : 0;
157 | }
158 |
159 | _maxVerticalBound() : number {
160 | if (this.props.tightBounds) return this.state.bounds.max;
161 | return (this.state.bounds.max > 0) ? this.state.bounds.max : 0;
162 | }
163 |
164 | render() {
165 | const components = { 'line': LineChart, 'bar': BarChart, 'pie': PieChart };
166 | const axisAlign = (this.props.type === 'line') ? 'left' : 'center';
167 | return (
168 |
169 | {(() => {
170 | const ChartType = components[this.props.type] || BarChart;
171 | if (this.props.showAxis && Chart !== PieChart) {
172 | return (
173 |
178 |
179 |
180 |
192 |
193 |
201 |
202 | {(() => {
203 | return (
204 |
205 |
213 |
214 | );
215 | })()}
216 |
217 | );
218 | }
219 | return (
220 |
225 |
233 |
234 | );
235 | })()}
236 |
237 | );
238 | }
239 | }
240 |
241 | Chart.propTypes = {
242 | // Shared properties between most types
243 | // data: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.array)).isRequired,
244 | type: PropTypes.oneOf(['line', 'bar', 'pie']).isRequired,
245 | highlightColor: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), // TODO
246 | highlightIndices: PropTypes.arrayOf(PropTypes.number), // TODO
247 | onDataPointPress: PropTypes.func,
248 | yAxisUseDecimal: PropTypes.bool,
249 | yAxisShortLabel: PropTypes.bool,
250 |
251 | // Bar chart props
252 | color: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.number, PropTypes.string])),
253 | cornerRadius: PropTypes.number,
254 | // fillGradient: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.number, PropTypes.string])), // TODO
255 | widthPercent: PropTypes.number,
256 |
257 | // Line/multi-line chart props
258 | fillColor: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), // need to adjust for multi-line
259 | dataPointColor: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.number, PropTypes.string])),
260 | dataPointFillColor: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.number, PropTypes.string])),
261 | dataPointRadius: PropTypes.number,
262 | // highlightRadius: PropTypes.number, // TODO
263 | lineWidth: PropTypes.number,
264 | showDataPoint: PropTypes.bool, // TODO
265 |
266 | // Pie chart props
267 | // pieCenterRatio: PropTypes.number, // TODO
268 | sliceColors: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.number, PropTypes.string])),
269 | animationDuration: PropTypes.number,
270 | axisColor: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
271 | axisLabelColor: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
272 | axisLineWidth: PropTypes.number,
273 | // axisTitleColor: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
274 | // axisTitleFontSize: PropTypes.number,
275 | // chartFontSize: PropTypes.number,
276 | // chartTitle: PropTypes.string,
277 | // chartTitleColor: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
278 | gridColor: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
279 | gridLineWidth: PropTypes.number,
280 | hideHorizontalGridLines: PropTypes.bool,
281 | hideVerticalGridLines: PropTypes.bool,
282 | // labelFontSize: PropTypes.number,
283 | showAxis: PropTypes.bool,
284 | showGrid: PropTypes.bool,
285 | showXAxisLabels: PropTypes.bool,
286 | showYAxisLabels: PropTypes.bool,
287 | style: PropTypes.any,
288 | tightBounds: PropTypes.bool,
289 | verticalGridStep: PropTypes.number,
290 | horizontalGridStep: PropTypes.number,
291 | // xAxisTitle: PropTypes.string,
292 | xAxisHeight: PropTypes.number,
293 | xAxisTransform: PropTypes.func,
294 | // yAxisTitle: PropTypes.string,
295 | yAxisTransform: PropTypes.func,
296 | yAxisWidth: PropTypes.number,
297 | };
298 |
--------------------------------------------------------------------------------
/dist/LineChart.js:
--------------------------------------------------------------------------------
1 | Object.defineProperty(exports,"__esModule",{value:true});var _slicedToArray=function(){function sliceIterator(arr,i){var _arr=[];var _n=true;var _d=false;var _e=undefined;try{for(var _i=arr[typeof Symbol==='function'?Symbol.iterator:'@@iterator'](),_s;!(_n=(_s=_i.next()).done);_n=true){_arr.push(_s.value);if(i&&_arr.length===i)break;}}catch(err){_d=true;_e=err;}finally {try{if(!_n&&_i["return"])_i["return"]();}finally {if(_d)throw _e;}}return _arr;}return function(arr,i){if(Array.isArray(arr)){return arr;}else if((typeof Symbol==='function'?Symbol.iterator:'@@iterator') in Object(arr)){return sliceIterator(arr,i);}else {throw new TypeError("Invalid attempt to destructure non-iterable instance");}};}();var _extends=Object.assign||function(target){for(var i=1;imax)max=number;});});var ceilMax=Math.ceil(max);var floorMin=Math.floor(min);if(ceilMax-floorMin>this.props.verticalGridStep){min=floorMin;max=ceilMax;} // Exit if we want tight bounds
152 | if(this.props.tightBounds){return this.setState({bounds:{min:min,max:max}});}max=getRoundNumber(max,this.props.verticalGridStep);if(min<0){var step=void 0;if(this.props.verticalGridStep>3){step=Math.abs(max-min)/(this.props.verticalGridStep-1);}else {step=Math.max(Math.abs(max-min)/2,Math.max(Math.abs(min),Math.abs(max)));}step=getRoundNumber(step,this.props.verticalGridStep);var newMin=void 0;var newMax=void 0;if(Math.abs(min)>Math.abs(max)){var m=Math.ceil(Math.abs(min)/step);newMin=step*m*(min>0?1:-1);newMax=step*(this.props.verticalGridStep-m)*(max>0?1:-1);}else {var _m=Math.ceil(Math.abs(max)/step);newMax=step*_m*(max>0?1:-1);newMin=step*(this.props.verticalGridStep-_m)*(min>0?1:-1);}if(minnewMax+step){newMin+=step;newMax+=step;}if(max0?this.state.bounds.min:0;}},{key:'_maxVerticalBound',value:function _maxVerticalBound()
156 |
157 |
158 | {
159 | if(this.props.tightBounds)return this.state.bounds.max;
160 | return this.state.bounds.max>0?this.state.bounds.max:0;}},{key:'render',value:function render()
161 |
162 |
163 | {var _this2=this;
164 | var components={'line':_LineChart2.default,'bar':_BarChart2.default,'pie':_PieChart2.default};
165 | var axisAlign=this.props.type==='line'?'left':'center';
166 | return (
167 | _react2.default.createElement(_reactNative.View,{__source:{fileName:_jsxFileName,lineNumber:167}},
168 | function(){
169 | var ChartType=components[_this2.props.type]||_BarChart2.default;
170 | if(_this2.props.showAxis&&Chart!==_PieChart2.default){
171 | return (
172 | _react2.default.createElement(_reactNative.View,{
173 | ref:'container',
174 | style:[_this2.props.style||{},{flex:1,flexDirection:'column'}],
175 | onLayout:_this2._onContainerLayout,__source:{fileName:_jsxFileName,lineNumber:172}},
176 |
177 | _react2.default.createElement(_reactNative.View,{style:[styles.default,{flexDirection:'row'}],__source:{fileName:_jsxFileName,lineNumber:177}},
178 | _react2.default.createElement(_reactNative.View,{ref:'yAxis',__source:{fileName:_jsxFileName,lineNumber:178}},
179 | _react2.default.createElement(_yAxis2.default,_extends({},
180 | _this2.props,{
181 | data:_this2.props.data,
182 | height:_this2.state.containerHeight-_this2.props.xAxisHeight,
183 | width:_this2.props.yAxisWidth,
184 | minVerticalBound:_this2.state.bounds.min,
185 | containerWidth:_this2.state.containerWidth,
186 | maxVerticalBound:_this2.state.bounds.max,
187 | yAxisUseDecimal:_this2.props.yAxisUseDecimal,
188 | yAxisShortLabel:_this2.props.yAxisShortLabel,
189 | style:{width:_this2.props.yAxisWidth},__source:{fileName:_jsxFileName,lineNumber:179}}))),
190 |
191 |
192 | _react2.default.createElement(ChartType,_extends({},
193 | _this2.props,{
194 | data:_this2.props.data,
195 | width:_this2.state.containerWidth-_this2.props.yAxisWidth,
196 | height:_this2.state.containerHeight-_this2.props.xAxisHeight,
197 | minVerticalBound:_this2.state.bounds.min,
198 | maxVerticalBound:_this2.state.bounds.max,__source:{fileName:_jsxFileName,lineNumber:192}}))),
199 |
200 |
201 | function(){
202 | return (
203 | _react2.default.createElement(_reactNative.View,{ref:'xAxis',__source:{fileName:_jsxFileName,lineNumber:203}},
204 | _react2.default.createElement(_xAxis2.default,_extends({},
205 | _this2.props,{
206 | width:_this2.state.containerWidth-_this2.props.yAxisWidth,
207 | data:_this2.props.data,
208 | height:_this2.props.xAxisHeight,
209 | align:axisAlign,
210 | style:{marginLeft:_this2.props.yAxisWidth-1},__source:{fileName:_jsxFileName,lineNumber:204}}))));}()));}
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 | return (
219 | _react2.default.createElement(_reactNative.View,{
220 | ref:'container',
221 | onLayout:_this2._onContainerLayout,
222 | style:[_this2.props.style||{},styles.default],__source:{fileName:_jsxFileName,lineNumber:219}},
223 |
224 | _react2.default.createElement(ChartType,_extends({},
225 | _this2.props,{
226 | data:_this2.props.data,
227 | width:_this2.state.containerWidth,
228 | height:_this2.state.containerHeight,
229 | minVerticalBound:_this2.state.bounds.min,
230 | maxVerticalBound:_this2.state.bounds.max,__source:{fileName:_jsxFileName,lineNumber:224}}))));}()));}}]);return Chart;}(_react.Component);Chart.defaultProps={data:[[]],animated:true,animationDuration:300,axisColor:C.BLACK,axisLabelColor:C.BLACK,axisLineWidth:1,axisTitleColor:C.GREY,axisTitleFontSize:16,chartFontSize:14,dataPointRadius:3,gridColor:C.BLACK,gridLineWidth:0.5,hideHorizontalGridLines:false,hideVerticalGridLines:false,horizontalScale:1,labelFontSize:10,lineWidth:1,showAxis:true,showDataPoint:false,showGrid:true,showXAxisLabels:true,showYAxisLabels:true,tightBounds:false,verticalGridStep:4,xAxisHeight:20,yAxisWidth:30,yAxisUseDecimal:false,yAxisShortLabel:false};exports.default=Chart;
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 | Chart.propTypes={
241 | // Shared properties between most types
242 | // data: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.array)).isRequired,
243 | type:_react.PropTypes.oneOf(['line','bar','pie']).isRequired,
244 | highlightColor:_react.PropTypes.oneOfType([_react.PropTypes.number,_react.PropTypes.string]), // TODO
245 | highlightIndices:_react.PropTypes.arrayOf(_react.PropTypes.number), // TODO
246 | onDataPointPress:_react.PropTypes.func,
247 | yAxisUseDecimal:_react.PropTypes.bool,
248 | yAxisShortLabel:_react.PropTypes.bool,
249 |
250 | // Bar chart props
251 | color:_react.PropTypes.arrayOf(_react.PropTypes.oneOfType([_react.PropTypes.number,_react.PropTypes.string])),
252 | cornerRadius:_react.PropTypes.number,
253 | // fillGradient: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.number, PropTypes.string])), // TODO
254 | widthPercent:_react.PropTypes.number,
255 |
256 | // Line/multi-line chart props
257 | fillColor:_react.PropTypes.oneOfType([_react.PropTypes.number,_react.PropTypes.string]), // need to adjust for multi-line
258 | dataPointColor:_react.PropTypes.arrayOf(_react.PropTypes.oneOfType([_react.PropTypes.number,_react.PropTypes.string])),
259 | dataPointFillColor:_react.PropTypes.arrayOf(_react.PropTypes.oneOfType([_react.PropTypes.number,_react.PropTypes.string])),
260 | dataPointRadius:_react.PropTypes.number,
261 | // highlightRadius: PropTypes.number, // TODO
262 | lineWidth:_react.PropTypes.number,
263 | showDataPoint:_react.PropTypes.bool, // TODO
264 |
265 | // Pie chart props
266 | // pieCenterRatio: PropTypes.number, // TODO
267 | sliceColors:_react.PropTypes.arrayOf(_react.PropTypes.oneOfType([_react.PropTypes.number,_react.PropTypes.string])),
268 | animationDuration:_react.PropTypes.number,
269 | axisColor:_react.PropTypes.oneOfType([_react.PropTypes.number,_react.PropTypes.string]),
270 | axisLabelColor:_react.PropTypes.oneOfType([_react.PropTypes.number,_react.PropTypes.string]),
271 | axisLineWidth:_react.PropTypes.number,
272 | // axisTitleColor: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
273 | // axisTitleFontSize: PropTypes.number,
274 | // chartFontSize: PropTypes.number,
275 | // chartTitle: PropTypes.string,
276 | // chartTitleColor: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
277 | gridColor:_react.PropTypes.oneOfType([_react.PropTypes.number,_react.PropTypes.string]),
278 | gridLineWidth:_react.PropTypes.number,
279 | hideHorizontalGridLines:_react.PropTypes.bool,
280 | hideVerticalGridLines:_react.PropTypes.bool,
281 | // labelFontSize: PropTypes.number,
282 | showAxis:_react.PropTypes.bool,
283 | showGrid:_react.PropTypes.bool,
284 | showXAxisLabels:_react.PropTypes.bool,
285 | showYAxisLabels:_react.PropTypes.bool,
286 | style:_react.PropTypes.any,
287 | tightBounds:_react.PropTypes.bool,
288 | verticalGridStep:_react.PropTypes.number,
289 | horizontalGridStep:_react.PropTypes.number,
290 | // xAxisTitle: PropTypes.string,
291 | xAxisHeight:_react.PropTypes.number,
292 | xAxisTransform:_react.PropTypes.func,
293 | // yAxisTitle: PropTypes.string,
294 | yAxisTransform:_react.PropTypes.func,
295 | yAxisWidth:_react.PropTypes.number};
--------------------------------------------------------------------------------