Skip to content

Commit ea512f5

Browse files
363 lines between labels and slices (#2734)
Co-authored-by: Kenan Yusuf <kenan.m.yusuf@gmail.com>
1 parent 4189d6c commit ea512f5

File tree

7 files changed

+289
-10
lines changed

7 files changed

+289
-10
lines changed

demo/js/components/victory-pie-demo.js

+16-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { random, range } from "lodash";
33
import React from "react";
44
import { VictoryPie } from "victory-pie";
55
import { VictoryTooltip } from "victory-tooltip";
6-
import { VictoryTheme } from "victory-core";
6+
import { VictoryTheme, LineSegment } from "victory-core";
77

88
export default class App extends React.Component {
99
constructor(props) {
@@ -228,7 +228,7 @@ export default class App extends React.Component {
228228
<VictoryPie
229229
style={{ ...this.state.style, labels: { fontSize: 0 } }}
230230
data={this.state.data}
231-
innerRadius={100}
231+
innerRadius={90}
232232
animate={{ duration: 2000 }}
233233
colorScale={this.state.colorScale}
234234
/>
@@ -317,6 +317,20 @@ export default class App extends React.Component {
317317
{ x: 8, y: 1, l: 315 },
318318
]}
319319
/>
320+
<VictoryPie
321+
style={{ parent: parentStyle }}
322+
labelIndicator
323+
/>
324+
<VictoryPie
325+
style={{ parent: parentStyle }}
326+
labelIndicator={<LineSegment style={{opacity:"1",strokeWidth:"1px",stroke: "red"}}/>}
327+
/>
328+
<VictoryPie
329+
style={{ parent: parentStyle }}
330+
labelIndicator={<LineSegment style={{opacity:"1",strokeWidth:"1px",strokeDasharray: "1",stroke: "red"}}/>}
331+
labelIndicatorInnerOffset={35}
332+
labelIndicatorOuterOffset={4}
333+
/>
320334
</div>
321335
</div>
322336
);

demo/ts/components/victory-pie-demo.tsx

+15-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from "react";
22
import { random, range } from "lodash";
33
import { VictoryPie } from "victory-pie";
44
import { VictoryTooltip } from "victory-tooltip";
5-
import { VictoryTheme } from "victory-core";
5+
import { VictoryTheme, LineSegment } from "victory-core";
66

77
interface VictoryPieDemoState {
88
data: {
@@ -306,6 +306,20 @@ export default class VictoryPieDemo extends React.Component<
306306
animate={{ duration: 2000 }}
307307
innerRadius={140}
308308
/>
309+
<VictoryPie
310+
style={{ parent: parentStyle }}
311+
labelIndicator
312+
/>
313+
<VictoryPie
314+
style={{ parent: parentStyle }}
315+
labelIndicator={<LineSegment style={{opacity:"1",strokeWidth:"1px",stroke: "red"}}/>}
316+
/>
317+
<VictoryPie
318+
style={{ parent: parentStyle }}
319+
labelIndicator={<LineSegment style={{opacity:"1",strokeWidth:"1px",strokeDasharray: "1",stroke: "red"}}/>}
320+
labelIndicatorInnerOffset={45}
321+
labelIndicatorOuterOffset={15}
322+
/>
309323
</div>
310324
</div>
311325
);

docs/src/content/docs/victory-pie.md

+56-1
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ The `labelPosition` prop specifies the position of each label relative to its co
292292

293293
`type: number || function`
294294

295-
The `labelRadius` prop defines the radius of the arc that will be used for positioning each slice label. If this prop is not set, the label radius will default to the radius of the pie + label padding. If this prop is given as a function, it will be evaluated for each label `VictoryPie` renders, and will be evaluated with the props that correspond to that label, as well as the radius and innerRadius of the corresponding slice.
295+
The `labelRadius` prop defines the radius of the arc that will be used for positioning each slice label. If this prop is not set, the label radius will default to the radius of the pie + label padding. If this prop is given as a function, it will be evaluated for each label `VictoryPie` renders, and will be evaluated with the props that correspond to that label, as well as the radius and innerRadius of the corresponding slice. If `labelIndicator` prop is being used, passed `labelRadius`(> radius) is used to calculate the co-ordinates of the outer indicator line. If no specific value for labelRadius is passed , default values will be considered. The outer indicator line length is the difference between `labelRadius` and `labelIndicatorOuterOffset`.
296296

297297
```playground
298298
<VictoryPie
@@ -519,6 +519,61 @@ See the [Data Accessors Guide][] for more detail on formatting and processing da
519519
y={(d) => d.value + d.error}
520520
```
521521

522+
## labelIndicator
523+
524+
`type: boolean || element`
525+
526+
The `labelIndicator` prop defines the label indicator line between labels and the pie chart. If this prop is used as a boolean,then the default indicator will be displayed. To customize or pass your own styling `<LineSegment/>` can be passed to labelIndicator. LabelIndicator is functional only when labelPosition = "centroid". To adjust the labelIndicator length, `labelIndicatorInnerOffset` and `labelIndicatorOuterOffset` props can be used alongside labelIndicator.
527+
528+
```playground
529+
<VictoryPie
530+
data={sampleData}
531+
labelIndicator
532+
style={{ labels: { fill: "white", fontSize: 20, fontWeight: "bold" } }}
533+
/>
534+
<VictoryPie
535+
data={sampleData}
536+
labelIndicator={<LineSegment style = {{stroke:"red", strokeDasharray:1,fill: "none",}}/>}
537+
style={{ labels: { fill: "white", fontSize: 20, fontWeight: "bold" } }}
538+
/>
539+
<VictoryPie
540+
data={sampleData}
541+
labelIndicator={<LineSegment style = {{stroke:"red", strokeDasharray:1,fill: "none",}}/>}
542+
style={{ labels: { fill: "white", fontSize: 20, fontWeight: "bold" } }}
543+
labelIndicatorInnerOffset={10}
544+
labelIndicatorOuterOffset={15}
545+
/>
546+
```
547+
## labelIndicatorInnerOffset
548+
549+
`type: number`
550+
551+
The `labelIndicatorInnerOffset` prop defines the offset by which the indicator length inside pie chart is being drawn. Higher the number shorter the length.
552+
553+
```playground
554+
<VictoryPie
555+
data={sampleData}
556+
labelIndicator
557+
style={{ labels: { fill: "white", fontSize: 20, fontWeight: "bold" } }}
558+
labelIndicatorInnerOffset={10}
559+
/>
560+
```
561+
562+
## labelIndicatorOuterOffset
563+
564+
`type: number`
565+
566+
The `labelIndicatorOuterOffset` prop defines the offset by which the indicator length outside the pie chart is being drawn. Higher the number shorter the length.
567+
568+
```playground
569+
<VictoryPie
570+
data={sampleData}
571+
labelIndicator
572+
style={{ labels: { fill: "white", fontSize: 20, fontWeight: "bold" } }}
573+
labelIndicatorOuterOffset={10}
574+
/>
575+
```
576+
522577
[animations guide]: /guides/animations
523578
[data accessors guide]: /guides/data-accessors
524579
[custom components guide]: /guides/custom-components

packages/victory-pie/src/helper-methods.js

+76-4
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,13 @@ const getLabelText = (props, datum, index) => {
100100
return checkForValidText(text);
101101
};
102102

103-
const getLabelArc = (radius, labelRadius, style) => {
103+
const getLabelArc = (labelRadius) => {
104+
return d3Shape.arc().outerRadius(labelRadius).innerRadius(labelRadius);
105+
};
106+
107+
const getCalculatedLabelRadius = (radius, labelRadius, style) => {
104108
const padding = (style && style.padding) || 0;
105-
const arcRadius = labelRadius || radius + padding;
106-
return d3Shape.arc().outerRadius(arcRadius).innerRadius(arcRadius);
109+
return labelRadius || radius + padding;
107110
};
108111

109112
const getLabelPosition = (arc, slice, position) => {
@@ -195,7 +198,12 @@ const getLabelProps = (text, dataProps, calculatedValues) => {
195198
labelStyle,
196199
assign({ labelRadius, text }, dataProps),
197200
);
198-
const labelArc = getLabelArc(defaultRadius, labelRadius, evaluatedStyle);
201+
const calculatedLabelRadius = getCalculatedLabelRadius(
202+
defaultRadius,
203+
labelRadius,
204+
evaluatedStyle,
205+
);
206+
const labelArc = getLabelArc(calculatedLabelRadius);
199207
const position = getLabelPosition(labelArc, slice, labelPosition);
200208
const baseAngle = getBaseLabelAngle(slice, labelPosition, labelStyle);
201209
const labelAngle = getLabelAngle(baseAngle, labelPlacement);
@@ -219,6 +227,7 @@ const getLabelProps = (text, dataProps, calculatedValues) => {
219227
textAnchor,
220228
verticalAnchor,
221229
angle: labelAngle,
230+
calculatedLabelRadius,
222231
};
223232

224233
if (!Helpers.isTooltip(labelComponent)) {
@@ -228,6 +237,57 @@ const getLabelProps = (text, dataProps, calculatedValues) => {
228237
return defaults({}, labelProps, Helpers.omit(tooltipTheme, ["style"]));
229238
};
230239

240+
export const getXOffsetMultiplayerByAngle = (angle) =>
241+
Math.cos(angle - Helpers.degreesToRadians(90));
242+
export const getYOffsetMultiplayerByAngle = (angle) =>
243+
Math.sin(angle - Helpers.degreesToRadians(90));
244+
export const getXOffset = (offset, angle) =>
245+
offset * getXOffsetMultiplayerByAngle(angle);
246+
export const getYOffset = (offset, angle) =>
247+
offset * getYOffsetMultiplayerByAngle(angle);
248+
export const getAverage = (array) =>
249+
array.reduce((acc, cur) => acc + cur, 0) / array.length;
250+
251+
export const getLabelIndicatorPropsForLineSegment = (
252+
props,
253+
calculatedValues,
254+
labelProps,
255+
) => {
256+
const {
257+
innerRadius,
258+
radius,
259+
slice: { startAngle, endAngle },
260+
labelIndicatorInnerOffset,
261+
labelIndicatorOuterOffset,
262+
index,
263+
} = props;
264+
265+
const { height, width } = calculatedValues;
266+
const { calculatedLabelRadius } = labelProps;
267+
// calculation
268+
const middleRadius = getAverage([innerRadius, radius]);
269+
const midAngle = getAverage([endAngle, startAngle]);
270+
const centerX = width / 2;
271+
const centerY = height / 2;
272+
const innerOffset = middleRadius + labelIndicatorInnerOffset;
273+
const outerOffset = calculatedLabelRadius - labelIndicatorOuterOffset;
274+
275+
const x1 = centerX + getXOffset(innerOffset, midAngle);
276+
const y1 = centerY + getYOffset(innerOffset, midAngle);
277+
278+
const x2 = centerX + getXOffset(outerOffset, midAngle);
279+
const y2 = centerY + getYOffset(outerOffset, midAngle);
280+
281+
const labelIndicatorProps = {
282+
x1,
283+
y1,
284+
x2,
285+
y2,
286+
index,
287+
};
288+
return defaults({}, labelIndicatorProps);
289+
};
290+
231291
export const getBaseProps = (initialProps, fallbackProps) => {
232292
const props = Helpers.modifyProps(initialProps, fallbackProps, "pie");
233293
const calculatedValues = getCalculatedValues(props);
@@ -248,6 +308,7 @@ export const getBaseProps = (initialProps, fallbackProps) => {
248308
cornerRadius,
249309
padAngle,
250310
disableInlineStyles,
311+
labelIndicator,
251312
} = calculatedValues;
252313
const radius = props.radius || defaultRadius;
253314
const initialChildProps = {
@@ -288,6 +349,17 @@ export const getBaseProps = (initialProps, fallbackProps) => {
288349
assign({}, props, dataProps),
289350
calculatedValues,
290351
);
352+
if (labelIndicator) {
353+
const labelProps = childProps[eventKey].labels;
354+
if (labelProps.calculatedLabelRadius > radius) {
355+
childProps[eventKey].labelIndicators =
356+
getLabelIndicatorPropsForLineSegment(
357+
assign({}, props, dataProps),
358+
calculatedValues,
359+
labelProps,
360+
);
361+
}
362+
}
291363
}
292364
return childProps;
293365
}, initialChildProps);

packages/victory-pie/src/index.d.ts

+3
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ export interface VictoryPieProps
6464
>[];
6565
eventKey?: StringOrNumberOrCallback;
6666
innerRadius?: NumberOrCallback;
67+
labelIndicator?: boolean | React.ReactElement;
68+
labelIndicatorInnerOffset: number;
69+
labelIndicatorOuterOffset: number;
6770
labelPlacement?:
6871
| VictorySliceLabelPlacementType
6972
| ((props: SliceProps) => VictorySliceLabelPlacementType);

0 commit comments

Comments
 (0)