Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvement/victory pie radius #1177

Merged
merged 13 commits into from
Nov 11, 2018
94 changes: 22 additions & 72 deletions demo/components/victory-pie-demo.js
Original file line number Diff line number Diff line change
@@ -1,51 +1,10 @@
/*global window:false*/
/*eslint-disable no-magic-numbers,react/no-multi-comp*/
import { merge, random, range } from "lodash";
import PropTypes from "prop-types";
import { random, range } from "lodash";
import React from "react";
import { VictoryPie, Slice } from "../../packages/victory-pie/src/index";
import { VictoryPie } from "../../packages/victory-pie/src/index";
import { VictoryTooltip } from "../../packages/victory-tooltip/src/index";
import {
VictoryContainer, VictoryTheme, VictoryLabel
} from "../../packages/victory-core/src/index";

class BorderLabelSlice extends React.Component {
static propTypes = {
...Slice.propTypes,
index: PropTypes.number
};

renderSlice(props) {
return <Slice {...props} />;
}

renderLabel(props) {
const { pathFunction, datum, slice, index, origin } = props;
const path = pathFunction({ ...slice, endAngle: slice.startAngle });
const pathId = `textPath-path-${index}`;
return (
<g transform={`translate(${origin.x}, ${origin.y})`}>
<path id={pathId} d={path} />
<text>
<textPath xlinkHref={`#${pathId}`}>
{datum.label || datum.xName || datum.x}
</textPath>
</text>
</g>
);
}

render() {
const { index } = this.props;

return (
<g key={`slice-and-label-${index}`}>
{this.renderSlice(this.props)}
{this.renderLabel(this.props)}
</g>
);
}
}
import { VictoryTheme } from "../../packages/victory-core/src/index";

export default class App extends React.Component {
constructor(props) {
Expand Down Expand Up @@ -145,6 +104,14 @@ export default class App extends React.Component {
labelRadius={60}
padding={{ bottom: 50, left: 50, right: 10 }}
width={400} height={200}
radius={(d) => d.radius}
data={[
{ x: 1, y: 1, radius: 40 },
{ x: 2, y: 3, radius: 50 },
{ x: 3, y: 5, radius: 70 },
{ x: 4, y: 2, radius: 80 },
{ x: 5, y: 3, radius: 60 }
]}
/>
<VictoryPie
style={{ parent: parentStyle }}
Expand Down Expand Up @@ -178,40 +145,24 @@ export default class App extends React.Component {
}
}}
data={this.state.transitionData}
containerComponent={
<VictoryContainer
title="Animated Pie Chart"
desc="This pie chart shows some data, which is described here."
/>}
/>

<VictoryPie
style={{
parent: { ...parentStyle, padding: "1% 3%" }
}}
style={{ parent: parentStyle }}
theme={VictoryTheme.material}
labelComponent={<VictoryLabel renderInPortal />}
labels={() => "click me!"}
events={[{
target: "data",
eventHandlers: {
onClick: () => {
return [
{
mutation: (props) => {
return {
style: merge({}, props.style, { fill: "#F50057" })
};
}
}, {
target: "labels",
eventKey: [0, 2, 4],
mutation: () => {
return { text: "Nice." };
}
}
];
}
onMouseOver: () => ({
mutation: (props) => ({
radius: 135,
sliceStartAngle: props.slice.startAngle + 0.05,
sliceEndAngle: props.slice.endAngle - 0.05
})
}),
onMouseOut: () => ({
mutation: () => null
})
}
}]}
/>
Expand Down Expand Up @@ -266,7 +217,6 @@ export default class App extends React.Component {
innerRadius={100}
animate={{ duration: 2000 }}
colorScale={this.state.colorScale}
dataComponent={<BorderLabelSlice />}
/>

<VictoryPie
Expand Down
4 changes: 2 additions & 2 deletions packages/victory-core/src/victory-util/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,11 @@ function evaluateStyle(style, data, active) {
}

function degreesToRadians(degrees) {
return degrees * (Math.PI / 180);
return typeof degrees === "number" ? degrees * (Math.PI / 180) : degrees;
}

function radiansToDegrees(radians) {
return radians / (Math.PI / 180);
return typeof radians === "number" ? radians / (Math.PI / 180) : radians;
}

function getRadius(props) {
Expand Down
46 changes: 24 additions & 22 deletions packages/victory-pie/src/helper-methods.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
/*eslint no-magic-numbers: ["error", { "ignore": [-1, 0, 1, 2, 45, 135, 180, 225, 315] }]*/
import { assign, isFunction, isPlainObject } from "lodash";
import { assign, defaults, isFunction, isPlainObject } from "lodash";
import * as d3Shape from "d3-shape";

import { Helpers, Data, Style } from "victory-core";

const degreesToRadians = (degrees) => {
return degrees * (Math.PI / 180);
};

const checkForValidText = (text) => {
if (text === undefined || text === null) {
return text;
Expand All @@ -24,7 +20,7 @@ const getColor = (style, colors, index) => {
};

const getRadius = (props, padding) => {
if (props.radius) {
if (typeof props.radius === "number") {
return props.radius;
}
return Math.min(
Expand All @@ -45,9 +41,9 @@ const getOrigin = (props, padding) => {
const getSlices = (props, data) => {
const layoutFunction = d3Shape.pie()
.sort(null)
.startAngle(degreesToRadians(props.startAngle))
.endAngle(degreesToRadians(props.endAngle))
.padAngle(degreesToRadians(props.padAngle))
.startAngle(Helpers.degreesToRadians(props.startAngle))
.endAngle(Helpers.degreesToRadians(props.endAngle))
.padAngle(Helpers.degreesToRadians(props.padAngle))
.value((datum) => { return datum._y; });
return layoutFunction(data);
};
Expand All @@ -58,15 +54,11 @@ const getCalculatedValues = (props) => {
const style = Helpers.getStyles(props.style, styleObject, "auto", "100%");
const colors = Array.isArray(colorScale) ? colorScale : Style.getColorScale(colorScale);
const padding = Helpers.getPadding(props);
const radius = getRadius(props, padding);
const defaultRadius = getRadius(props, padding);
const origin = getOrigin(props, padding);
const data = Data.getData(props);
const slices = getSlices(props, data);
const pathFunction = d3Shape.arc()
.cornerRadius(props.cornerRadius)
.outerRadius(radius)
.innerRadius(props.innerRadius);
return { style, colors, padding, radius, data, slices, pathFunction, origin };
return { style, colors, padding, defaultRadius, data, slices, origin };
};

const getSliceStyle = (index, calculatedValues) => {
Expand Down Expand Up @@ -138,12 +130,12 @@ const getVerticalAnchor = (orientation) => {

const getLabelProps = (props, dataProps, calculatedValues) => {
const { index, datum, data, slice } = dataProps;
const { style, radius, origin } = calculatedValues;
const { style, defaultRadius, origin } = calculatedValues;
const labelStyle = Helpers.evaluateStyle(
assign({ padding: 0 }, style.labels), datum, props.active
);
const labelRadius = Helpers.evaluateProp(props.labelRadius, datum);
const labelArc = getLabelArc(radius, labelRadius, labelStyle);
const labelArc = getLabelArc(defaultRadius, labelRadius, labelStyle);
const position = getLabelPosition(labelArc, slice, props.labelPosition);
const orientation = getLabelOrientation(slice);
return {
Expand All @@ -161,17 +153,27 @@ const getLabelProps = (props, dataProps, calculatedValues) => {
export const getBaseProps = (props, fallbackProps) => {
props = Helpers.modifyProps(props, fallbackProps, "pie");
const calculatedValues = getCalculatedValues(props);
const { slices, style, pathFunction, data, origin } = calculatedValues;
const { labels, events, sharedEvents, height, width, standalone, name } = props;
const { slices, style, data, origin, defaultRadius } = calculatedValues;
const {
labels, events, sharedEvents, height, width, standalone, name,
innerRadius, cornerRadius, padAngle
} = props;
const radius = props.radius || defaultRadius;
const initialChildProps = {
parent: { standalone, height, width, slices, pathFunction, name, style: style.parent }
parent: { standalone, height, width, slices, name, style: style.parent }
};

return slices.reduce((childProps, slice, index) => {
const datum = data[index];
const datum = defaults(
{}, data[index], {
startAngle: Helpers.radiansToDegrees(slice.startAngle),
endAngle: Helpers.radiansToDegrees(slice.endAngle),
padAngle: Helpers.radiansToDegrees(slice.padAngle)
}
);
const eventKey = datum.eventKey || index;
const dataProps = {
index, slice, pathFunction, datum, data, origin,
index, slice, datum, data, origin, innerRadius, radius, cornerRadius, padAngle,
style: getSliceStyle(index, calculatedValues)
};
childProps[eventKey] = {
Expand Down
43 changes: 38 additions & 5 deletions packages/victory-pie/src/slice.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,65 @@
import React from "react";
import PropTypes from "prop-types";
import { Helpers, CommonProps, Path } from "victory-core";
import { isFunction } from "lodash";
import { defaults, isFunction } from "lodash";
import * as d3Shape from "d3-shape";


export default class Slice extends React.Component {
static propTypes = {
...CommonProps.primitiveProps,
cornerRadius: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),
datum: PropTypes.object,
innerRadius: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),
padAngle: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),
pathComponent: PropTypes.element,
pathFunction: PropTypes.func,
slice: PropTypes.object
radius: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),
slice: PropTypes.object,
sliceEndAngle: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),
sliceStartAngle: PropTypes.oneOfType([PropTypes.number, PropTypes.func])

};

static defaultProps = {
pathComponent: <Path/>
};

getPath(props) {
const { datum, active, slice } = props;
if (isFunction(props.pathFunction)) {
return props.pathFunction(slice);
}
const cornerRadius = Helpers.evaluateProp(props.cornerRadius, datum, active);
const innerRadius = Helpers.evaluateProp(props.innerRadius, datum, active);
const radius = Helpers.evaluateProp(props.radius, datum, active);
const padAngle = Helpers.degreesToRadians(
Helpers.evaluateProp(props.padAngle, datum, active)
);
const startAngle = Helpers.degreesToRadians(
Helpers.evaluateProp(props.sliceStartAngle, datum, active)
);
const endAngle = Helpers.degreesToRadians(
Helpers.evaluateProp(props.sliceEndAngle, datum, active)
);
const pathFunction = d3Shape.arc()
.cornerRadius(cornerRadius)
.outerRadius(radius)
.innerRadius(innerRadius);
return pathFunction(defaults({ startAngle, endAngle, padAngle }, slice));
}

render() {
const {
datum, slice, active, role, shapeRendering, className,
origin, events, pathComponent, pathFunction, style, clipPath
datum, active, role, shapeRendering, className,
origin, events, pathComponent, style, clipPath
} = this.props;
const defaultTransform = origin ? `translate(${origin.x}, ${origin.y})` : undefined;
const transform = this.props.transform || defaultTransform;
return React.cloneElement(pathComponent, {
className, role, shapeRendering, events, transform, clipPath,
style: Helpers.evaluateStyle(style, datum, active),
d: isFunction(pathFunction) ? pathFunction(slice) : undefined
d: this.getPath(this.props)
});
}
}
12 changes: 7 additions & 5 deletions packages/victory-pie/src/victory-pie.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ class VictoryPie extends React.Component {
onEnter: {
duration: 500,
before: () => ({ _y: 0, label: " " }),
after: (datum) => ({ y_: datum._y, label: datum.label })
after: (datum) => ({
y_: datum._y, label: datum.label
})
}
};

Expand All @@ -62,7 +64,7 @@ class VictoryPie extends React.Component {
])
]),
containerComponent: PropTypes.element,
cornerRadius: CustomPropTypes.nonNegative,
cornerRadius: PropTypes.oneOfType([ CustomPropTypes.nonNegative, PropTypes.func ]),
data: PropTypes.array,
dataComponent: PropTypes.element,
endAngle: PropTypes.number,
Expand Down Expand Up @@ -99,7 +101,7 @@ class VictoryPie extends React.Component {
})),
groupComponent: PropTypes.element,
height: CustomPropTypes.nonNegative,
innerRadius: CustomPropTypes.nonNegative,
innerRadius: PropTypes.oneOfType([ CustomPropTypes.nonNegative, PropTypes.func ]),
labelComponent: PropTypes.element,
labelPosition: PropTypes.oneOf(["startAngle", "centroid", "endAngle"]),
labelRadius: PropTypes.oneOfType([ CustomPropTypes.nonNegative, PropTypes.func ]),
Expand All @@ -109,15 +111,15 @@ class VictoryPie extends React.Component {
x: CustomPropTypes.nonNegative,
y: CustomPropTypes.nonNegative
}),
padAngle: CustomPropTypes.nonNegative,
padAngle: PropTypes.oneOfType([ CustomPropTypes.nonNegative, PropTypes.func ]),
padding: PropTypes.oneOfType([
PropTypes.number,
PropTypes.shape({
top: PropTypes.number, bottom: PropTypes.number,
left: PropTypes.number, right: PropTypes.number
})
]),
radius: CustomPropTypes.nonNegative,
radius: PropTypes.oneOfType([ CustomPropTypes.nonNegative, PropTypes.func ]),
sharedEvents: PropTypes.shape({
events: PropTypes.array,
getEventState: PropTypes.func
Expand Down
Loading