Skip to content

Commit

Permalink
363 lines between labels and slices (#2734)
Browse files Browse the repository at this point in the history
Co-authored-by: Kenan Yusuf <[email protected]>
  • Loading branch information
acharyakavita and KenanYusuf authored Jan 24, 2024
1 parent 4189d6c commit ea512f5
Show file tree
Hide file tree
Showing 7 changed files with 289 additions and 10 deletions.
18 changes: 16 additions & 2 deletions demo/js/components/victory-pie-demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { random, range } from "lodash";
import React from "react";
import { VictoryPie } from "victory-pie";
import { VictoryTooltip } from "victory-tooltip";
import { VictoryTheme } from "victory-core";
import { VictoryTheme, LineSegment } from "victory-core";

export default class App extends React.Component {
constructor(props) {
Expand Down Expand Up @@ -228,7 +228,7 @@ export default class App extends React.Component {
<VictoryPie
style={{ ...this.state.style, labels: { fontSize: 0 } }}
data={this.state.data}
innerRadius={100}
innerRadius={90}
animate={{ duration: 2000 }}
colorScale={this.state.colorScale}
/>
Expand Down Expand Up @@ -317,6 +317,20 @@ export default class App extends React.Component {
{ x: 8, y: 1, l: 315 },
]}
/>
<VictoryPie
style={{ parent: parentStyle }}
labelIndicator
/>
<VictoryPie
style={{ parent: parentStyle }}
labelIndicator={<LineSegment style={{opacity:"1",strokeWidth:"1px",stroke: "red"}}/>}
/>
<VictoryPie
style={{ parent: parentStyle }}
labelIndicator={<LineSegment style={{opacity:"1",strokeWidth:"1px",strokeDasharray: "1",stroke: "red"}}/>}
labelIndicatorInnerOffset={35}
labelIndicatorOuterOffset={4}
/>
</div>
</div>
);
Expand Down
16 changes: 15 additions & 1 deletion demo/ts/components/victory-pie-demo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from "react";
import { random, range } from "lodash";
import { VictoryPie } from "victory-pie";
import { VictoryTooltip } from "victory-tooltip";
import { VictoryTheme } from "victory-core";
import { VictoryTheme, LineSegment } from "victory-core";

interface VictoryPieDemoState {
data: {
Expand Down Expand Up @@ -306,6 +306,20 @@ export default class VictoryPieDemo extends React.Component<
animate={{ duration: 2000 }}
innerRadius={140}
/>
<VictoryPie
style={{ parent: parentStyle }}
labelIndicator
/>
<VictoryPie
style={{ parent: parentStyle }}
labelIndicator={<LineSegment style={{opacity:"1",strokeWidth:"1px",stroke: "red"}}/>}
/>
<VictoryPie
style={{ parent: parentStyle }}
labelIndicator={<LineSegment style={{opacity:"1",strokeWidth:"1px",strokeDasharray: "1",stroke: "red"}}/>}
labelIndicatorInnerOffset={45}
labelIndicatorOuterOffset={15}
/>
</div>
</div>
);
Expand Down
57 changes: 56 additions & 1 deletion docs/src/content/docs/victory-pie.md
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ The `labelPosition` prop specifies the position of each label relative to its co

`type: number || function`

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.
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`.

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

## labelIndicator

`type: boolean || element`

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.

```playground
<VictoryPie
data={sampleData}
labelIndicator
style={{ labels: { fill: "white", fontSize: 20, fontWeight: "bold" } }}
/>
<VictoryPie
data={sampleData}
labelIndicator={<LineSegment style = {{stroke:"red", strokeDasharray:1,fill: "none",}}/>}
style={{ labels: { fill: "white", fontSize: 20, fontWeight: "bold" } }}
/>
<VictoryPie
data={sampleData}
labelIndicator={<LineSegment style = {{stroke:"red", strokeDasharray:1,fill: "none",}}/>}
style={{ labels: { fill: "white", fontSize: 20, fontWeight: "bold" } }}
labelIndicatorInnerOffset={10}
labelIndicatorOuterOffset={15}
/>
```
## labelIndicatorInnerOffset

`type: number`

The `labelIndicatorInnerOffset` prop defines the offset by which the indicator length inside pie chart is being drawn. Higher the number shorter the length.

```playground
<VictoryPie
data={sampleData}
labelIndicator
style={{ labels: { fill: "white", fontSize: 20, fontWeight: "bold" } }}
labelIndicatorInnerOffset={10}
/>
```

## labelIndicatorOuterOffset

`type: number`

The `labelIndicatorOuterOffset` prop defines the offset by which the indicator length outside the pie chart is being drawn. Higher the number shorter the length.

```playground
<VictoryPie
data={sampleData}
labelIndicator
style={{ labels: { fill: "white", fontSize: 20, fontWeight: "bold" } }}
labelIndicatorOuterOffset={10}
/>
```

[animations guide]: /guides/animations
[data accessors guide]: /guides/data-accessors
[custom components guide]: /guides/custom-components
Expand Down
80 changes: 76 additions & 4 deletions packages/victory-pie/src/helper-methods.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,13 @@ const getLabelText = (props, datum, index) => {
return checkForValidText(text);
};

const getLabelArc = (radius, labelRadius, style) => {
const getLabelArc = (labelRadius) => {
return d3Shape.arc().outerRadius(labelRadius).innerRadius(labelRadius);
};

const getCalculatedLabelRadius = (radius, labelRadius, style) => {
const padding = (style && style.padding) || 0;
const arcRadius = labelRadius || radius + padding;
return d3Shape.arc().outerRadius(arcRadius).innerRadius(arcRadius);
return labelRadius || radius + padding;
};

const getLabelPosition = (arc, slice, position) => {
Expand Down Expand Up @@ -195,7 +198,12 @@ const getLabelProps = (text, dataProps, calculatedValues) => {
labelStyle,
assign({ labelRadius, text }, dataProps),
);
const labelArc = getLabelArc(defaultRadius, labelRadius, evaluatedStyle);
const calculatedLabelRadius = getCalculatedLabelRadius(
defaultRadius,
labelRadius,
evaluatedStyle,
);
const labelArc = getLabelArc(calculatedLabelRadius);
const position = getLabelPosition(labelArc, slice, labelPosition);
const baseAngle = getBaseLabelAngle(slice, labelPosition, labelStyle);
const labelAngle = getLabelAngle(baseAngle, labelPlacement);
Expand All @@ -219,6 +227,7 @@ const getLabelProps = (text, dataProps, calculatedValues) => {
textAnchor,
verticalAnchor,
angle: labelAngle,
calculatedLabelRadius,
};

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

export const getXOffsetMultiplayerByAngle = (angle) =>
Math.cos(angle - Helpers.degreesToRadians(90));
export const getYOffsetMultiplayerByAngle = (angle) =>
Math.sin(angle - Helpers.degreesToRadians(90));
export const getXOffset = (offset, angle) =>
offset * getXOffsetMultiplayerByAngle(angle);
export const getYOffset = (offset, angle) =>
offset * getYOffsetMultiplayerByAngle(angle);
export const getAverage = (array) =>
array.reduce((acc, cur) => acc + cur, 0) / array.length;

export const getLabelIndicatorPropsForLineSegment = (
props,
calculatedValues,
labelProps,
) => {
const {
innerRadius,
radius,
slice: { startAngle, endAngle },
labelIndicatorInnerOffset,
labelIndicatorOuterOffset,
index,
} = props;

const { height, width } = calculatedValues;
const { calculatedLabelRadius } = labelProps;
// calculation
const middleRadius = getAverage([innerRadius, radius]);
const midAngle = getAverage([endAngle, startAngle]);
const centerX = width / 2;
const centerY = height / 2;
const innerOffset = middleRadius + labelIndicatorInnerOffset;
const outerOffset = calculatedLabelRadius - labelIndicatorOuterOffset;

const x1 = centerX + getXOffset(innerOffset, midAngle);
const y1 = centerY + getYOffset(innerOffset, midAngle);

const x2 = centerX + getXOffset(outerOffset, midAngle);
const y2 = centerY + getYOffset(outerOffset, midAngle);

const labelIndicatorProps = {
x1,
y1,
x2,
y2,
index,
};
return defaults({}, labelIndicatorProps);
};

export const getBaseProps = (initialProps, fallbackProps) => {
const props = Helpers.modifyProps(initialProps, fallbackProps, "pie");
const calculatedValues = getCalculatedValues(props);
Expand All @@ -248,6 +308,7 @@ export const getBaseProps = (initialProps, fallbackProps) => {
cornerRadius,
padAngle,
disableInlineStyles,
labelIndicator,
} = calculatedValues;
const radius = props.radius || defaultRadius;
const initialChildProps = {
Expand Down Expand Up @@ -288,6 +349,17 @@ export const getBaseProps = (initialProps, fallbackProps) => {
assign({}, props, dataProps),
calculatedValues,
);
if (labelIndicator) {
const labelProps = childProps[eventKey].labels;
if (labelProps.calculatedLabelRadius > radius) {
childProps[eventKey].labelIndicators =
getLabelIndicatorPropsForLineSegment(
assign({}, props, dataProps),
calculatedValues,
labelProps,
);
}
}
}
return childProps;
}, initialChildProps);
Expand Down
3 changes: 3 additions & 0 deletions packages/victory-pie/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ export interface VictoryPieProps
>[];
eventKey?: StringOrNumberOrCallback;
innerRadius?: NumberOrCallback;
labelIndicator?: boolean | React.ReactElement;
labelIndicatorInnerOffset: number;
labelIndicatorOuterOffset: number;
labelPlacement?:
| VictorySliceLabelPlacementType
| ((props: SliceProps) => VictorySliceLabelPlacementType);
Expand Down
Loading

1 comment on commit ea512f5

@vercel
Copy link

@vercel vercel bot commented on ea512f5 Jan 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.