Skip to content
Draft
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
58f9f9a
Fix linear gradient issue due to `react-native-svg` lib update
aboveyunhai Jun 9, 2020
82b71a3
reimplement core chart, support bar-chart for full dynamic sizes
aboveyunhai Jun 15, 2020
162e676
Merge pull request #1 from aboveyunhai/dynamic-chart-size
aboveyunhai Jun 15, 2020
a82b7b8
Update README.ME for chartStyle
aboveyunhai Jun 15, 2020
7fad3a7
re-invent heatmap to fully dynamy and layout-control
aboveyunhai Jun 16, 2020
780a7f8
Merge pull request #2 from aboveyunhai/dynamic-chart-size
aboveyunhai Jun 16, 2020
ecd718d
Update README.md
aboveyunhai Jun 16, 2020
1e2c5b5
fully responsive support for line-chart, All padding includes.
aboveyunhai Jun 23, 2020
9be8679
Add tooltip to contribution chart onPressIn and onPressOut
aboveyunhai Jun 23, 2020
17001b0
Merge and fix conflicts
aboveyunhai Jun 23, 2020
d32fa5a
Merge pull request #3 from aboveyunhai/dynamic-chart-size
aboveyunhai Jun 23, 2020
1be57b2
Update abstract-chart compatibility for line-chart, shift default GRA…
aboveyunhai Jun 23, 2020
f093150
Merge pull request #4 from aboveyunhai/dynamic-chart-size
aboveyunhai Jun 23, 2020
49ed337
Fix: correct midpoint for BarChart labels
aboveyunhai Jun 23, 2020
5ce937b
Merge pull request #5 from aboveyunhai/dynamic-chart-size
aboveyunhai Jun 23, 2020
5910ef6
Fix: did not update when data changes
aboveyunhai Jul 6, 2020
5c70620
Merge pull request #6 from aboveyunhai/dynamic-chart-size
aboveyunhai Jul 6, 2020
bfb53c8
correct data display on scrollableInfo
aboveyunhai Jul 7, 2020
5c4fd0f
Merge pull request #7 from aboveyunhai/dynamic-chart-size
aboveyunhai Jul 7, 2020
6caba94
seperate hideLabel from hidePointsArIndex. correct vertical line hieg…
aboveyunhai Jul 23, 2020
8e540f4
Merge pull request #8 from aboveyunhai/dynamic-chart-size
aboveyunhai Jul 23, 2020
3cd41b8
Update index.d.ts
aboveyunhai Jul 30, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,17 @@ const chartConfig = {
color: (opacity = 1) => `rgba(26, 255, 146, ${opacity})`,
strokeWidth: 2, // optional, default 3
barPercentage: 0.5,
useShadowColorFromDataset: false // optional
useShadowColorFromDataset: false, // optional
gutterTop: 10, // optional, default dynamic size: 10% * innerHeight after paddingTop and paddingBottom
horizontalLabelWidth: 30, // optional, default dynamic size:20% * innerHeight after paddingTop and paddingBottom
verticalLabelHeight: 30, // optional, default dynamic size: 15% * innerWidth after paddingLeft and paddingRight
chartStyle: { //optional
borderRadius: 10, //default 0
paddingTop: 10, //default 0
paddingBottom: 10,
paddingLeft: 10,
paddingRight: 10,
},
};
```

Expand All @@ -108,6 +118,9 @@ const chartConfig = {
| barRadius | Number | Defines the radius of each bar |
| propsForBackgroundLines | props | Override styles of the background lines, refer to react-native-svg's Line documentation |
| propsForLabels | props | Override styles of the labels, refer to react-native-svg's Text documentation |
| gutterTop | number | Define the gap between highest coordinate and padding |
| horizontalLabelWidth | number | Define the width of horizontal labels |
| verticalLabelHeight | number | Define the height of vertical labels |

## Responsive charts

Expand Down Expand Up @@ -415,14 +428,23 @@ const commitsData = [
chartConfig={chartConfig}
/>
```
Extra chartStyle for heatmap
```js
const chartConfig = {
chartStyle: {
justifyContent: 'start' || 'center' || 'end', //optional, defualt is 'start';
alignItems: 'start' || 'center' || 'end', //optional, default is 'start';
},
};
```

| Property | Type | Description |
| ------------------ | -------- | ------------------------------------------------------------------------------------------- |
| data | Object | Data for the chart - see example above |
| width | Number | Width of the chart, use 'Dimensions' library to get the width of your screen for responsive |
| height | Number | Height of the chart |
| gutterSize | Number | Size of the gutters between the squares in the chart |
| squareSize | Number | Size of the squares in the chart |
| squareSize | Number | Optional, Size of the squares in the chart, dynamic size will be auto applied if prop is not provided |
| horizontal | boolean | Should graph be laid out horizontally? Defaults to `true` |
| showMonthLabels | boolean | Should graph include labels for the months? Defaults to `true` |
| showOutOfRangeDays | boolean | Should graph be filled with squares, including days outside the range? Defaults to `false` |
Expand Down
9 changes: 8 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,8 @@ export interface BarChartProps {
data: ChartData;
width: number;
height: number;
fromZero?: boolean;
defMax?: number;
defMin?: number;
withInnerLines?: boolean;
yAxisLabel: string;
yAxisSuffix: string;
Expand All @@ -244,6 +245,8 @@ export interface BarChartProps {
segments?: number;
showBarTops?: boolean;
showValuesOnTopOfBars?: boolean;
withHorizontalLabels?: boolean;
withVerticalLabels?: boolean;
}

export class BarChart extends React.Component<BarChartProps> {}
Expand Down Expand Up @@ -306,6 +309,7 @@ export class PieChart extends React.Component<PieChartProps> {}

// ContributionGraph
export interface ContributionGraphProps {
style?: ViewStyle;
values: Array<any>;
endDate: Date;
numDays: number;
Expand All @@ -320,6 +324,9 @@ export interface ContributionGraphProps {
accessor?: string;
getMonthLabel?: (monthIndex: number) => string;
onDayPress?: ({ count: number, date: Date }) => void;
toggleTooltip?: boolean;
tooltipContent?: (dateInfo: { date: string, [accessor: string]: string},
args: {x:number, y:number, index:number}) => JSX.Element;
}

export class ContributionGraph extends React.Component<
Expand Down
182 changes: 87 additions & 95 deletions src/abstract-chart.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@ import React, { Component } from "react";
import { LinearGradient, Line, Text, Defs, Stop } from "react-native-svg";

class AbstractChart extends Component {

calcScaler = data => {
if (this.props.fromZero) {
return Math.max(...data, 0) - Math.min(...data, 0) || 1;
} else {
return Math.max(...data) - Math.min(...data) || 1;
}
const defMin = this.props.defMin ?? Math.min(...data);
const defMax = this.props.defMax ?? Math.max(...data);
return Math.max(...data, defMin, defMax) - Math.min(...data, defMin, defMax) || 1;
};

calcBaseHeight = (data, height) => {
const min = Math.min(...data);
const max = Math.max(...data);
const defMin = this.props.defMin ?? Math.min(...data);
const defMax = this.props.defMax ?? Math.max(...data);
const min = Math.min(...data, defMin, defMax);
const max = Math.max(...data, defMin, defMax);
if (min >= 0 && max >= 0) {
return height;
} else if (min < 0 && max <= 0) {
Expand All @@ -24,18 +25,16 @@ class AbstractChart extends Component {
};

calcHeight = (val, data, height) => {
const max = Math.max(...data);
const min = Math.min(...data);
const defMin = this.props.defMin ?? Math.min(...data);
const defMax = this.props.defMax ?? Math.max(...data);
const max = Math.max(...data, defMin, defMax);
const min = Math.min(...data, defMin, defMax);
if (min < 0 && max > 0) {
return height * (val / this.calcScaler(data));
} else if (min >= 0 && max >= 0) {
return this.props.fromZero
? height * (val / this.calcScaler(data))
: height * ((val - min) / this.calcScaler(data));
return height * ((val - min) / this.calcScaler(data));
} else if (min < 0 && max <= 0) {
return this.props.fromZero
? height * (val / this.calcScaler(data))
: height * ((val - max) / this.calcScaler(data));
return height * ((val - max) / this.calcScaler(data));
}
};

Expand All @@ -58,88 +57,77 @@ class AbstractChart extends Component {
return {
fontSize: 12,
fill: labelColor(0.8),
...propsForLabels
...propsForLabels,
};
}

renderHorizontalLines = config => {
const { count, width, height, paddingTop, paddingRight } = config;
const basePosition = height - height / 4;

return [...new Array(count + 1)].map((_, i) => {
const y = (basePosition / count) * i + paddingTop;
const { count, width, height, gutterTop, horizontalLabelWidth, verticalLabelHeight,
chartStyle: { paddingTop, paddingLeft, paddingRight, paddingBottom },
} = config;
const basePosition = height - verticalLabelHeight - paddingBottom;
const totalLineHeight = basePosition - paddingTop - gutterTop;
const x1 = horizontalLabelWidth + paddingLeft;
const x2 = width - paddingRight;
const lineGap = (count - 1) || 1; //handle divided by zero
return [...new Array(count)].map((_, i) => {
const y = basePosition - totalLineHeight / lineGap * i
return (
<Line
key={Math.random()}
x1={paddingRight}
x1={x1}
y1={y}
x2={width}
x2={x2}
y2={y}
{...this.getPropsForBackgroundLines()}
/>
);
});
};

renderHorizontalLine = config => {
const { width, height, paddingTop, paddingRight } = config;
return (
<Line
key={Math.random()}
x1={paddingRight}
y1={height - height / 4 + paddingTop}
x2={width}
y2={height - height / 4 + paddingTop}
{...this.getPropsForBackgroundLines()}
/>
);
};

renderHorizontalLabels = config => {
const {
count,
data,
height,
paddingTop,
paddingRight,
gutterTop,
horizontalLabelWidth,
horizontalLabelRotation = 0,
verticalLabelHeight,
decimalPlaces = 2,
formatYLabel = yLabel => yLabel
formatYLabel = yLabel => yLabel,
chartStyle: { paddingTop, paddingLeft, paddingRight, paddingBottom },
} = config;

const {
yAxisLabel = "",
yAxisSuffix = "",
yLabelsOffset = 12
yLabelsOffset = 12,
defMin = Math.min(...data),
defMax = Math.max(...data),
} = this.props;

return [...Array(count === 1 ? 1 : count + 1).keys()].map((i, _) => {
const basePosition = height - verticalLabelHeight - paddingBottom;
const totalLineHeight = basePosition - paddingTop - gutterTop;
const lineGap = (count - 1) || 1;

return [...Array(count === 1 ? 1 : count).keys()].map((i, _) => {
let yLabel = i * count;

if (count === 1) {
yLabel = `${yAxisLabel}${formatYLabel(
data[0].toFixed(decimalPlaces)
)}${yAxisSuffix}`;
} else {
const label = this.props.fromZero
? (this.calcScaler(data) / count) * i + Math.min(...data, 0)
: (this.calcScaler(data) / count) * i + Math.min(...data);
yLabel = `${yAxisLabel}${formatYLabel(
label.toFixed(decimalPlaces)
)}${yAxisSuffix}`;
}
const label = this.calcScaler(data) / lineGap * i + Math.min(...data, defMin, defMax);
yLabel = `${yAxisLabel}${formatYLabel(
label.toFixed(decimalPlaces)
)}${yAxisSuffix}`;

const x = horizontalLabelWidth - yLabelsOffset;
const y = basePosition - totalLineHeight / lineGap * i;

const basePosition = height - height / 4;
const x = paddingRight - yLabelsOffset;
const y =
count === 1 && this.props.fromZero
? paddingTop + 4
: (height * 3) / 4 - (basePosition / count) * i + paddingTop;
return (
<Text
rotation={horizontalLabelRotation}
origin={`${x}, ${y}`}
key={Math.random()}
x={x}
x={x + paddingLeft}
textAnchor="end"
y={y}
{...this.getPropsForLabels()}
Expand All @@ -155,33 +143,39 @@ class AbstractChart extends Component {
labels = [],
width,
height,
paddingRight,
paddingTop,
horizontalLabelWidth,
verticalLabelHeight,
gutterTop,
horizontalOffset = 0,
stackedBar = false,
verticalLabelRotation = 0,
formatXLabel = xLabel => xLabel
formatXLabel = xLabel => xLabel,
chartStyle: { paddingTop, paddingLeft, paddingRight, paddingBottom },
midPoint = 0,
} = config;

const {
xAxisLabel = "",
xLabelsOffset = 0,
hidePointsAtIndex = []
hideLabelsAtIndex = []
} = this.props;
const fontSize = 12;
let fac = 1;
if (stackedBar) {
fac = 0.71;
}

const labelWidth = (width - horizontalLabelWidth - paddingRight - paddingLeft) / labels.length;

const y = height - paddingBottom - verticalLabelHeight + xLabelsOffset + fontSize*1.5;

return labels.map((label, i) => {
if (hidePointsAtIndex.includes(i)) {
if (hideLabelsAtIndex.includes(i)) {
return null;
}
const x =
(((width - paddingRight) / labels.length) * i +
paddingRight +
horizontalOffset) *
fac;
const y = (height * 3) / 4 + paddingTop + fontSize * 2 + xLabelsOffset;

const x = (paddingLeft + horizontalLabelWidth + labelWidth * i + midPoint + horizontalOffset) * fac;

return (
<Text
origin={`${x}, ${y}`}
Expand All @@ -199,44 +193,40 @@ class AbstractChart extends Component {
};

renderVerticalLines = config => {
const { data, width, height, paddingTop, paddingRight } = config;
const { yAxisInterval = 1 } = this.props;
return [...new Array(Math.ceil(data.length / yAxisInterval))].map(
const { data, width, height, gutterTop, horizontalLabelWidth, verticalLabelHeight,
chartStyle: { paddingTop, paddingLeft, paddingRight, paddingBottom },
} = config;
const {
yAxisInterval = 1,
adjustment = 1,
innerLines,
} = this.props;
const innerWidth = width - horizontalLabelWidth - paddingLeft - paddingRight;

const lineNum = innerLines || data.length;

const gap = innerWidth / (lineNum / yAxisInterval);

return [...new Array(Math.ceil(lineNum / yAxisInterval))].map(
(_, i) => {
return (
<Line
key={Math.random()}
x1={Math.floor(
((width - paddingRight) / (data.length / yAxisInterval)) * i +
paddingRight
gap * i * adjustment + horizontalLabelWidth + paddingLeft
)}
y1={0}
y1={paddingTop+gutterTop}
x2={Math.floor(
((width - paddingRight) / (data.length / yAxisInterval)) * i +
paddingRight
gap * i * adjustment + horizontalLabelWidth + paddingLeft
)}
y2={height - height / 4 + paddingTop}
y2={height - verticalLabelHeight - paddingBottom }
{...this.getPropsForBackgroundLines()}
/>
);
}
);
};

renderVerticalLine = config => {
const { height, paddingTop, paddingRight } = config;
return (
<Line
key={Math.random()}
x1={Math.floor(paddingRight)}
y1={0}
x2={Math.floor(paddingRight)}
y2={height - height / 4 + paddingTop}
{...this.getPropsForBackgroundLines()}
/>
);
};

renderDefs = config => {
const {
width,
Expand All @@ -246,9 +236,11 @@ class AbstractChart extends Component {
useShadowColorFromDataset,
data
} = config;

const fromOpacity = config.hasOwnProperty("backgroundGradientFromOpacity")
? config.backgroundGradientFromOpacity
: 1.0;

const toOpacity = config.hasOwnProperty("backgroundGradientToOpacity")
? config.backgroundGradientToOpacity
: 1.0;
Expand Down
Loading