victory-pie 30.6.1 → 31.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,13 +1,9 @@
1
1
  /*eslint no-magic-numbers: ["error", { "ignore": [-1, 0, 1, 2, 45, 135, 180, 225, 315] }]*/
2
- import { assign, isFunction, isPlainObject } from "lodash";
2
+ import { assign, defaults, isFunction, isPlainObject, isNil } from "lodash";
3
3
  import * as d3Shape from "d3-shape";
4
4
 
5
5
  import { Helpers, Data, Style } from "victory-core";
6
6
 
7
- const degreesToRadians = (degrees) => {
8
- return degrees * (Math.PI / 180);
9
- };
10
-
11
7
  const checkForValidText = (text) => {
12
8
  if (text === undefined || text === null) {
13
9
  return text;
@@ -24,13 +20,15 @@ const getColor = (style, colors, index) => {
24
20
  };
25
21
 
26
22
  const getRadius = (props, padding) => {
27
- if (props.radius) {
23
+ if (typeof props.radius === "number") {
28
24
  return props.radius;
29
25
  }
30
- return Math.min(
31
- props.width - padding.left - padding.right,
32
- props.height - padding.top - padding.bottom
33
- ) / 2;
26
+ return (
27
+ Math.min(
28
+ props.width - padding.left - padding.right,
29
+ props.height - padding.top - padding.bottom
30
+ ) / 2
31
+ );
34
32
  };
35
33
 
36
34
  const getOrigin = (props, padding) => {
@@ -43,12 +41,15 @@ const getOrigin = (props, padding) => {
43
41
  };
44
42
 
45
43
  const getSlices = (props, data) => {
46
- const layoutFunction = d3Shape.pie()
44
+ const layoutFunction = d3Shape
45
+ .pie()
47
46
  .sort(null)
48
- .startAngle(degreesToRadians(props.startAngle))
49
- .endAngle(degreesToRadians(props.endAngle))
50
- .padAngle(degreesToRadians(props.padAngle))
51
- .value((datum) => { return datum._y; });
47
+ .startAngle(Helpers.degreesToRadians(props.startAngle))
48
+ .endAngle(Helpers.degreesToRadians(props.endAngle))
49
+ .padAngle(Helpers.degreesToRadians(props.padAngle))
50
+ .value((datum) => {
51
+ return datum._y;
52
+ });
52
53
  return layoutFunction(data);
53
54
  };
54
55
 
@@ -58,15 +59,11 @@ const getCalculatedValues = (props) => {
58
59
  const style = Helpers.getStyles(props.style, styleObject, "auto", "100%");
59
60
  const colors = Array.isArray(colorScale) ? colorScale : Style.getColorScale(colorScale);
60
61
  const padding = Helpers.getPadding(props);
61
- const radius = getRadius(props, padding);
62
+ const defaultRadius = getRadius(props, padding);
62
63
  const origin = getOrigin(props, padding);
63
64
  const data = Data.getData(props);
64
65
  const slices = getSlices(props, data);
65
- const pathFunction = d3Shape.arc()
66
- .cornerRadius(props.cornerRadius)
67
- .outerRadius(radius)
68
- .innerRadius(props.innerRadius);
69
- return { style, colors, padding, radius, data, slices, pathFunction, origin };
66
+ return { style, colors, padding, defaultRadius, data, slices, origin };
70
67
  };
71
68
 
72
69
  const getSliceStyle = (index, calculatedValues) => {
@@ -88,9 +85,10 @@ const getLabelText = (props, datum, index) => {
88
85
  };
89
86
 
90
87
  const getLabelArc = (radius, labelRadius, style) => {
91
- const padding = style && style.padding || 0;
88
+ const padding = (style && style.padding) || 0;
92
89
  const arcRadius = labelRadius || radius + padding;
93
- return d3Shape.arc()
90
+ return d3Shape
91
+ .arc()
94
92
  .outerRadius(arcRadius)
95
93
  .innerRadius(arcRadius);
96
94
  };
@@ -138,16 +136,22 @@ const getVerticalAnchor = (orientation) => {
138
136
 
139
137
  const getLabelProps = (props, dataProps, calculatedValues) => {
140
138
  const { index, datum, data, slice } = dataProps;
141
- const { style, radius, origin } = calculatedValues;
139
+ const { style, defaultRadius, origin } = calculatedValues;
142
140
  const labelStyle = Helpers.evaluateStyle(
143
- assign({ padding: 0 }, style.labels), datum, props.active
141
+ assign({ padding: 0 }, style.labels),
142
+ datum,
143
+ props.active
144
144
  );
145
145
  const labelRadius = Helpers.evaluateProp(props.labelRadius, datum);
146
- const labelArc = getLabelArc(radius, labelRadius, labelStyle);
146
+ const labelArc = getLabelArc(defaultRadius, labelRadius, labelStyle);
147
147
  const position = getLabelPosition(labelArc, slice, props.labelPosition);
148
148
  const orientation = getLabelOrientation(slice);
149
149
  return {
150
- index, datum, data, slice, orientation,
150
+ index,
151
+ datum,
152
+ data,
153
+ slice,
154
+ orientation,
151
155
  style: labelStyle,
152
156
  x: Math.round(position[0]) + origin.x,
153
157
  y: Math.round(position[1]) + origin.y,
@@ -161,24 +165,48 @@ const getLabelProps = (props, dataProps, calculatedValues) => {
161
165
  export const getBaseProps = (props, fallbackProps) => {
162
166
  props = Helpers.modifyProps(props, fallbackProps, "pie");
163
167
  const calculatedValues = getCalculatedValues(props);
164
- const { slices, style, pathFunction, data, origin } = calculatedValues;
165
- const { labels, events, sharedEvents, height, width, standalone, name } = props;
168
+ const { slices, style, data, origin, defaultRadius } = calculatedValues;
169
+ const {
170
+ labels,
171
+ events,
172
+ sharedEvents,
173
+ height,
174
+ width,
175
+ standalone,
176
+ name,
177
+ innerRadius,
178
+ cornerRadius,
179
+ padAngle
180
+ } = props;
181
+ const radius = props.radius || defaultRadius;
166
182
  const initialChildProps = {
167
- parent: { standalone, height, width, slices, pathFunction, name, style: style.parent }
183
+ parent: { standalone, height, width, slices, name, style: style.parent }
168
184
  };
169
185
 
170
186
  return slices.reduce((childProps, slice, index) => {
171
- const datum = data[index];
172
- const eventKey = datum.eventKey || index;
187
+ const datum = defaults({}, data[index], {
188
+ startAngle: Helpers.radiansToDegrees(slice.startAngle),
189
+ endAngle: Helpers.radiansToDegrees(slice.endAngle),
190
+ padAngle: Helpers.radiansToDegrees(slice.padAngle)
191
+ });
192
+ const eventKey = !isNil(datum.eventKey) ? datum.eventKey : index;
173
193
  const dataProps = {
174
- index, slice, pathFunction, datum, data, origin,
194
+ index,
195
+ slice,
196
+ datum,
197
+ data,
198
+ origin,
199
+ innerRadius,
200
+ radius,
201
+ cornerRadius,
202
+ padAngle,
175
203
  style: getSliceStyle(index, calculatedValues)
176
204
  };
177
205
  childProps[eventKey] = {
178
206
  data: dataProps
179
207
  };
180
208
  const text = getLabelText(props, datum, index);
181
- if (text !== undefined && text !== null || (labels && (events || sharedEvents))) {
209
+ if ((text !== undefined && text !== null) || (labels && (events || sharedEvents))) {
182
210
  childProps[eventKey].labels = getLabelProps(props, dataProps, calculatedValues);
183
211
  }
184
212
  return childProps;
package/src/slice.js CHANGED
@@ -1,32 +1,75 @@
1
1
  import React from "react";
2
2
  import PropTypes from "prop-types";
3
3
  import { Helpers, CommonProps, Path } from "victory-core";
4
- import { isFunction } from "lodash";
4
+ import { defaults, isFunction } from "lodash";
5
+ import * as d3Shape from "d3-shape";
5
6
 
6
7
  export default class Slice extends React.Component {
7
8
  static propTypes = {
8
9
  ...CommonProps.primitiveProps,
10
+ cornerRadius: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),
9
11
  datum: PropTypes.object,
12
+ innerRadius: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),
13
+ padAngle: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),
10
14
  pathComponent: PropTypes.element,
11
15
  pathFunction: PropTypes.func,
12
- slice: PropTypes.object
16
+ radius: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),
17
+ slice: PropTypes.object,
18
+ sliceEndAngle: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),
19
+ sliceStartAngle: PropTypes.oneOfType([PropTypes.number, PropTypes.func])
13
20
  };
14
21
 
15
22
  static defaultProps = {
16
- pathComponent: <Path/>
23
+ pathComponent: <Path />
17
24
  };
18
25
 
26
+ getPath(props) {
27
+ const { datum, active, slice } = props;
28
+ if (isFunction(props.pathFunction)) {
29
+ return props.pathFunction(slice);
30
+ }
31
+ const cornerRadius = Helpers.evaluateProp(props.cornerRadius, datum, active);
32
+ const innerRadius = Helpers.evaluateProp(props.innerRadius, datum, active);
33
+ const radius = Helpers.evaluateProp(props.radius, datum, active);
34
+ const padAngle = Helpers.degreesToRadians(Helpers.evaluateProp(props.padAngle, datum, active));
35
+ const startAngle = Helpers.degreesToRadians(
36
+ Helpers.evaluateProp(props.sliceStartAngle, datum, active)
37
+ );
38
+ const endAngle = Helpers.degreesToRadians(
39
+ Helpers.evaluateProp(props.sliceEndAngle, datum, active)
40
+ );
41
+ const pathFunction = d3Shape
42
+ .arc()
43
+ .cornerRadius(cornerRadius)
44
+ .outerRadius(radius)
45
+ .innerRadius(innerRadius);
46
+ return pathFunction(defaults({ startAngle, endAngle, padAngle }, slice));
47
+ }
48
+
19
49
  render() {
20
50
  const {
21
- datum, slice, active, role, shapeRendering, className,
22
- origin, events, pathComponent, pathFunction, style, clipPath
51
+ datum,
52
+ active,
53
+ role,
54
+ shapeRendering,
55
+ className,
56
+ origin,
57
+ events,
58
+ pathComponent,
59
+ style,
60
+ clipPath
23
61
  } = this.props;
24
62
  const defaultTransform = origin ? `translate(${origin.x}, ${origin.y})` : undefined;
25
63
  const transform = this.props.transform || defaultTransform;
26
64
  return React.cloneElement(pathComponent, {
27
- className, role, shapeRendering, events, transform, clipPath,
65
+ className,
66
+ role,
67
+ shapeRendering,
68
+ events,
69
+ transform,
70
+ clipPath,
28
71
  style: Helpers.evaluateStyle(style, datum, active),
29
- d: isFunction(pathFunction) ? pathFunction(slice) : undefined
72
+ d: this.getPath(this.props)
30
73
  });
31
74
  }
32
75
  }
@@ -2,7 +2,12 @@
2
2
  import React from "react";
3
3
  import PropTypes from "prop-types";
4
4
  import {
5
- addEvents, Helpers, Data, PropTypes as CustomPropTypes, VictoryContainer, VictoryLabel,
5
+ addEvents,
6
+ Helpers,
7
+ Data,
8
+ PropTypes as CustomPropTypes,
9
+ VictoryContainer,
10
+ VictoryLabel,
6
11
  VictoryTheme
7
12
  } from "victory-core";
8
13
  import Slice from "./slice";
@@ -31,12 +36,21 @@ const fallbackProps = {
31
36
  labelPosition: "centroid"
32
37
  };
33
38
 
34
- const animationWhitelist = [
35
- "data", "endAngle", "height", "innerRadius", "cornerRadius", "padAngle", "padding",
36
- "colorScale", "startAngle", "style", "width"
37
- ];
38
-
39
39
  class VictoryPie extends React.Component {
40
+ static animationWhitelist = [
41
+ "data",
42
+ "endAngle",
43
+ "height",
44
+ "innerRadius",
45
+ "cornerRadius",
46
+ "padAngle",
47
+ "padding",
48
+ "colorScale",
49
+ "startAngle",
50
+ "style",
51
+ "width"
52
+ ];
53
+
40
54
  static displayName = "VictoryPie";
41
55
 
42
56
  static role = "pie";
@@ -49,7 +63,10 @@ class VictoryPie extends React.Component {
49
63
  onEnter: {
50
64
  duration: 500,
51
65
  before: () => ({ _y: 0, label: " " }),
52
- after: (datum) => ({ y_: datum._y, label: datum.label })
66
+ after: (datum) => ({
67
+ y_: datum._y,
68
+ label: datum.label
69
+ })
53
70
  }
54
71
  };
55
72
 
@@ -58,11 +75,18 @@ class VictoryPie extends React.Component {
58
75
  colorScale: PropTypes.oneOfType([
59
76
  PropTypes.arrayOf(PropTypes.string),
60
77
  PropTypes.oneOf([
61
- "grayscale", "qualitative", "heatmap", "warm", "cool", "red", "green", "blue"
78
+ "grayscale",
79
+ "qualitative",
80
+ "heatmap",
81
+ "warm",
82
+ "cool",
83
+ "red",
84
+ "green",
85
+ "blue"
62
86
  ])
63
87
  ]),
64
88
  containerComponent: PropTypes.element,
65
- cornerRadius: CustomPropTypes.nonNegative,
89
+ cornerRadius: PropTypes.oneOfType([CustomPropTypes.nonNegative, PropTypes.func]),
66
90
  data: PropTypes.array,
67
91
  dataComponent: PropTypes.element,
68
92
  endAngle: PropTypes.number,
@@ -71,53 +95,53 @@ class VictoryPie extends React.Component {
71
95
  CustomPropTypes.allOfType([CustomPropTypes.integer, CustomPropTypes.nonNegative]),
72
96
  PropTypes.string
73
97
  ]),
74
- events: PropTypes.arrayOf(PropTypes.shape({
75
- target: PropTypes.oneOf(["data", "labels", "parent"]),
76
- eventKey: PropTypes.oneOfType([
77
- PropTypes.func,
78
- CustomPropTypes.allOfType([CustomPropTypes.integer, CustomPropTypes.nonNegative]),
79
- PropTypes.string
80
- ]),
81
- eventHandlers: PropTypes.object
82
- })),
83
- externalEventMutations: PropTypes.arrayOf(PropTypes.shape({
84
- callback: PropTypes.function,
85
- childName: PropTypes.oneOfType([
86
- PropTypes.string,
87
- PropTypes.array
88
- ]),
89
- eventKey: PropTypes.oneOfType([
90
- PropTypes.array,
91
- CustomPropTypes.allOfType([CustomPropTypes.integer, CustomPropTypes.nonNegative]),
92
- PropTypes.string
93
- ]),
94
- mutation: PropTypes.function,
95
- target: PropTypes.oneOfType([
96
- PropTypes.string,
97
- PropTypes.array
98
- ])
99
- })),
98
+ events: PropTypes.arrayOf(
99
+ PropTypes.shape({
100
+ target: PropTypes.oneOf(["data", "labels", "parent"]),
101
+ eventKey: PropTypes.oneOfType([
102
+ PropTypes.func,
103
+ CustomPropTypes.allOfType([CustomPropTypes.integer, CustomPropTypes.nonNegative]),
104
+ PropTypes.string
105
+ ]),
106
+ eventHandlers: PropTypes.object
107
+ })
108
+ ),
109
+ externalEventMutations: PropTypes.arrayOf(
110
+ PropTypes.shape({
111
+ callback: PropTypes.function,
112
+ childName: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
113
+ eventKey: PropTypes.oneOfType([
114
+ PropTypes.array,
115
+ CustomPropTypes.allOfType([CustomPropTypes.integer, CustomPropTypes.nonNegative]),
116
+ PropTypes.string
117
+ ]),
118
+ mutation: PropTypes.function,
119
+ target: PropTypes.oneOfType([PropTypes.string, PropTypes.array])
120
+ })
121
+ ),
100
122
  groupComponent: PropTypes.element,
101
123
  height: CustomPropTypes.nonNegative,
102
- innerRadius: CustomPropTypes.nonNegative,
124
+ innerRadius: PropTypes.oneOfType([CustomPropTypes.nonNegative, PropTypes.func]),
103
125
  labelComponent: PropTypes.element,
104
126
  labelPosition: PropTypes.oneOf(["startAngle", "centroid", "endAngle"]),
105
- labelRadius: PropTypes.oneOfType([ CustomPropTypes.nonNegative, PropTypes.func ]),
106
- labels: PropTypes.oneOfType([ PropTypes.func, PropTypes.array ]),
127
+ labelRadius: PropTypes.oneOfType([CustomPropTypes.nonNegative, PropTypes.func]),
128
+ labels: PropTypes.oneOfType([PropTypes.func, PropTypes.array]),
107
129
  name: PropTypes.string,
108
130
  origin: PropTypes.shape({
109
131
  x: CustomPropTypes.nonNegative,
110
132
  y: CustomPropTypes.nonNegative
111
133
  }),
112
- padAngle: CustomPropTypes.nonNegative,
134
+ padAngle: PropTypes.oneOfType([CustomPropTypes.nonNegative, PropTypes.func]),
113
135
  padding: PropTypes.oneOfType([
114
136
  PropTypes.number,
115
137
  PropTypes.shape({
116
- top: PropTypes.number, bottom: PropTypes.number,
117
- left: PropTypes.number, right: PropTypes.number
138
+ top: PropTypes.number,
139
+ bottom: PropTypes.number,
140
+ left: PropTypes.number,
141
+ right: PropTypes.number
118
142
  })
119
143
  ]),
120
- radius: CustomPropTypes.nonNegative,
144
+ radius: PropTypes.oneOfType([CustomPropTypes.nonNegative, PropTypes.func]),
121
145
  sharedEvents: PropTypes.shape({
122
146
  events: PropTypes.array,
123
147
  getEventState: PropTypes.func
@@ -132,7 +156,9 @@ class VictoryPie extends React.Component {
132
156
  standalone: PropTypes.bool,
133
157
  startAngle: PropTypes.number,
134
158
  style: PropTypes.shape({
135
- parent: PropTypes.object, data: PropTypes.object, labels: PropTypes.object
159
+ parent: PropTypes.object,
160
+ data: PropTypes.object,
161
+ labels: PropTypes.object
136
162
  }),
137
163
  theme: PropTypes.object,
138
164
  width: CustomPropTypes.nonNegative,
@@ -159,10 +185,10 @@ class VictoryPie extends React.Component {
159
185
  { x: "E", y: 2 }
160
186
  ],
161
187
  standalone: true,
162
- dataComponent: <Slice/>,
163
- labelComponent: <VictoryLabel/>,
164
- containerComponent: <VictoryContainer/>,
165
- groupComponent: <g/>,
188
+ dataComponent: <Slice />,
189
+ labelComponent: <VictoryLabel />,
190
+ containerComponent: <VictoryContainer />,
191
+ groupComponent: <g />,
166
192
  sortOrder: "ascending",
167
193
  theme: VictoryTheme.grayscale
168
194
  };
@@ -170,7 +196,10 @@ class VictoryPie extends React.Component {
170
196
  static getBaseProps = (props) => getBaseProps(props, fallbackProps);
171
197
  static getData = Data.getData;
172
198
  static expectedComponents = [
173
- "dataComponent", "labelComponent", "groupComponent", "containerComponent"
199
+ "dataComponent",
200
+ "labelComponent",
201
+ "groupComponent",
202
+ "containerComponent"
174
203
  ];
175
204
 
176
205
  // Overridden in victory-native
@@ -179,8 +208,9 @@ class VictoryPie extends React.Component {
179
208
  }
180
209
 
181
210
  render() {
182
- const { role } = this.constructor;
211
+ const { animationWhitelist, role } = VictoryPie;
183
212
  const props = Helpers.modifyProps(this.props, fallbackProps, role);
213
+
184
214
  if (this.shouldAnimate()) {
185
215
  return this.animateComponent(props, animationWhitelist);
186
216
  }