victory-pie
Version:
Pie Component for Victory
366 lines (363 loc) • 13.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getYOffsetMultiplayerByAngle = exports.getYOffset = exports.getXOffsetMultiplayerByAngle = exports.getXOffset = exports.getLabelIndicatorPropsForLineSegment = exports.getBaseProps = exports.getAverage = void 0;
var _defaults = _interopRequireDefault(require("lodash/defaults"));
var _isPlainObject = _interopRequireDefault(require("lodash/isPlainObject"));
var d3Shape = _interopRequireWildcard(require("victory-vendor/d3-shape"));
var _victoryCore = require("victory-core");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/* eslint no-magic-numbers: ["error", { "ignore": [-1, 0, 1, 2, 45, 90, 135, 180, 225, 270, 315, 360] }]*/
const checkForValidText = text => {
if (text === undefined || text === null || _victoryCore.Helpers.isFunction(text)) {
return text;
}
return `${text}`;
};
const getColor = (style, colors, index) => {
if (style && style.data && style.data.fill) {
return style.data.fill;
}
return colors && colors[index % colors.length];
};
const getRadius = (props, padding) => {
if (typeof props.radius === "number") {
return props.radius;
}
return Math.min(props.width - padding.left - padding.right, props.height - padding.top - padding.bottom) / 2;
};
const getOrigin = (props, padding) => {
const {
width,
height
} = props;
const origin = (0, _isPlainObject.default)(props.origin) ? props.origin : {};
return {
x: origin.x !== undefined ? origin.x : (padding.left - padding.right + width) / 2,
y: origin.y !== undefined ? origin.y : (padding.top - padding.bottom + height) / 2
};
};
const getSlices = (props, data) => {
const padAngle = _victoryCore.Helpers.isFunction(props.padAngle) ? 0 : props.padAngle;
const layoutFunction = d3Shape.pie().sort(null).startAngle(_victoryCore.Helpers.degreesToRadians(props.startAngle)).endAngle(_victoryCore.Helpers.degreesToRadians(props.endAngle)).padAngle(_victoryCore.Helpers.degreesToRadians(padAngle)).value(datum => {
return datum._y;
});
return layoutFunction(data);
};
const getCategoriesFromProps = props => Array.isArray(props.categories) ? props.categories : props?.categories?.x ?? [];
/**
* Sorts data by props.categories or props.categories.x. If all of the data keys aren't
* included in categories, any remaining data will be appended to the data array.
* If extraneous categories are included in the categories prop, the will be ignored and
* have no effect on the rendered component.
*/
const getDataSortedByCategories = (props, data) => {
const sorted = [];
getCategoriesFromProps(props).forEach(category => {
const idx = data.findIndex(_ref => {
let {
x
} = _ref;
return x === category;
});
if (idx >= 0) {
const datum = data.splice(idx, 1)[0];
sorted.push(datum);
}
});
return [...sorted, ...data];
};
const getCalculatedValues = props => {
const {
colorScale,
theme
} = props;
const styleObject = _victoryCore.Helpers.getDefaultStyles(props, "pie");
const style = _victoryCore.Helpers.getStyles(props.style, styleObject);
const colors = Array.isArray(colorScale) ? colorScale : _victoryCore.Style.getColorScale(colorScale, theme);
const padding = _victoryCore.Helpers.getPadding(props.padding);
const defaultRadius = getRadius(props, padding);
const origin = getOrigin(props, padding);
const data = getDataSortedByCategories(props, _victoryCore.Data.getData(props));
const slices = getSlices(props, data);
return Object.assign({}, props, {
style,
colors,
padding,
defaultRadius,
data,
slices,
origin
});
};
const getSliceStyle = (index, calculatedValues) => {
const {
style,
colors
} = calculatedValues;
const fill = getColor(style, colors, index);
return Object.assign({
fill
}, style.data);
};
const getLabelText = (props, datum, index) => {
let text;
if (datum.label) {
text = datum.label;
} else if (Array.isArray(props.labels)) {
text = props.labels[index];
} else {
text = _victoryCore.Helpers.isFunction(props.labels) ? props.labels : datum.xName || datum._x;
}
return checkForValidText(text);
};
const getLabelArc = labelRadius => {
return d3Shape.arc().outerRadius(labelRadius).innerRadius(labelRadius);
};
const getCalculatedLabelRadius = (radius, labelRadius, style) => {
const padding = style && style.padding || 0;
return labelRadius || radius + padding;
};
const getLabelPosition = (arc, slice, position) => {
const construct = {
startAngle: position === "startAngle" ? slice.startAngle : slice.endAngle,
endAngle: position === "endAngle" ? slice.endAngle : slice.startAngle
};
const clonedArc = Object.assign({}, slice, construct);
return arc.centroid(clonedArc);
};
const getLabelOrientation = (degree, labelPlacement) => {
if (labelPlacement === "perpendicular") {
return degree > 90 && degree < 270 ? "bottom" : "top";
} else if (labelPlacement === "parallel") {
return degree >= 0 && degree <= 180 ? "right" : "left";
}
if (degree < 45 || degree > 315) {
return "top";
} else if (degree >= 45 && degree < 135) {
return "right";
} else if (degree >= 135 && degree < 225) {
return "bottom";
}
return "left";
};
const getTextAnchor = orientation => {
if (orientation === "top" || orientation === "bottom") {
return "middle";
}
return orientation === "right" ? "start" : "end";
};
const getVerticalAnchor = orientation => {
if (orientation === "left" || orientation === "right") {
return "middle";
}
return orientation === "bottom" ? "start" : "end";
};
const getBaseLabelAngle = (slice, labelPosition, labelStyle) => {
let baseAngle = 0;
if (labelPosition.angle !== undefined) {
baseAngle = labelStyle.angle;
} else if (labelPosition === "centroid") {
baseAngle = _victoryCore.Helpers.radiansToDegrees((slice.startAngle + slice.endAngle) / 2);
} else {
baseAngle = labelPosition === "startAngle" ? _victoryCore.Helpers.radiansToDegrees(slice.startAngle) : _victoryCore.Helpers.radiansToDegrees(slice.endAngle);
}
const positiveAngle = baseAngle < 0 ? 360 - baseAngle : baseAngle;
return positiveAngle % 360;
};
const getLabelAngle = (baseAngle, labelPlacement) => {
if (labelPlacement === "vertical") {
return 0;
}
if (labelPlacement === "parallel") {
return baseAngle > 180 && baseAngle < 360 ? baseAngle + 90 : baseAngle - 90;
}
return baseAngle > 90 && baseAngle < 270 ? baseAngle - 180 : baseAngle;
};
const getLabelProps = (text, dataProps, calculatedValues) => {
const {
index,
datum,
data,
slice,
labelComponent,
theme
} = dataProps;
const {
style,
defaultRadius,
origin,
width,
height
} = calculatedValues;
const labelRadius = _victoryCore.Helpers.evaluateProp(calculatedValues.labelRadius, Object.assign({
text
}, dataProps));
const labelPosition = _victoryCore.Helpers.evaluateProp(calculatedValues.labelPosition, Object.assign({
text
}, dataProps)) || "centroid";
const labelPlacement = _victoryCore.Helpers.evaluateProp(calculatedValues.labelPlacement, Object.assign({
text
}, dataProps)) || "vertical";
const labelStyle = Object.assign({
padding: 0
}, style.labels);
const evaluatedStyle = _victoryCore.Helpers.evaluateStyle(labelStyle, Object.assign({
labelRadius,
text
}, dataProps));
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);
const orientation = getLabelOrientation(baseAngle, labelPlacement);
const textAnchor = labelStyle.textAnchor || getTextAnchor(orientation);
const verticalAnchor = labelStyle.verticalAnchor || getVerticalAnchor(orientation);
const labelProps = {
width,
height,
index,
datum,
data,
slice,
orientation,
text,
style: labelStyle,
x: Math.round(position[0]) + origin.x,
y: Math.round(position[1]) + origin.y,
textAnchor,
verticalAnchor,
angle: labelAngle,
calculatedLabelRadius
};
if (!_victoryCore.Helpers.isTooltip(labelComponent)) {
return labelProps;
}
const tooltipTheme = theme && theme.tooltip || {};
return (0, _defaults.default)({}, labelProps, _victoryCore.Helpers.omit(tooltipTheme, ["style"]));
};
const getXOffsetMultiplayerByAngle = angle => Math.cos(angle - _victoryCore.Helpers.degreesToRadians(90));
exports.getXOffsetMultiplayerByAngle = getXOffsetMultiplayerByAngle;
const getYOffsetMultiplayerByAngle = angle => Math.sin(angle - _victoryCore.Helpers.degreesToRadians(90));
exports.getYOffsetMultiplayerByAngle = getYOffsetMultiplayerByAngle;
const getXOffset = (offset, angle) => offset * getXOffsetMultiplayerByAngle(angle);
exports.getXOffset = getXOffset;
const getYOffset = (offset, angle) => offset * getYOffsetMultiplayerByAngle(angle);
exports.getYOffset = getYOffset;
const getAverage = array => array.reduce((acc, cur) => acc + cur, 0) / array.length;
exports.getAverage = getAverage;
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 (0, _defaults.default)({}, labelIndicatorProps);
};
exports.getLabelIndicatorPropsForLineSegment = getLabelIndicatorPropsForLineSegment;
const getBaseProps = (initialProps, fallbackProps) => {
const props = _victoryCore.Helpers.modifyProps(initialProps, fallbackProps, "pie");
const calculatedValues = getCalculatedValues(props);
const {
slices,
style,
data,
origin,
defaultRadius,
labels,
events,
sharedEvents,
height,
width,
standalone,
name,
innerRadius,
cornerRadius,
padAngle,
disableInlineStyles,
labelIndicator
} = calculatedValues;
const radius = props.radius || defaultRadius;
const initialChildProps = {
parent: {
standalone,
height,
width,
slices,
name,
style: style.parent
}
};
return slices.reduce((childProps, slice, index) => {
const datum = (0, _defaults.default)({}, data[index], {
startAngle: _victoryCore.Helpers.radiansToDegrees(slice.startAngle),
endAngle: _victoryCore.Helpers.radiansToDegrees(slice.endAngle),
padAngle: _victoryCore.Helpers.radiansToDegrees(slice.padAngle)
});
const eventKey = !_victoryCore.Helpers.isNil(datum.eventKey) ? datum.eventKey : index;
const dataProps = {
index,
slice,
datum,
data,
origin,
innerRadius,
radius,
cornerRadius,
padAngle,
style: disableInlineStyles ? {} : getSliceStyle(index, calculatedValues),
disableInlineStyles
};
childProps[eventKey] = {
data: dataProps
};
const text = getLabelText(props, datum, index);
if (text !== undefined && text !== null || labels && (events || sharedEvents)) {
const evaluatedText = _victoryCore.Helpers.evaluateProp(text, dataProps);
childProps[eventKey].labels = getLabelProps(evaluatedText, Object.assign({}, props, dataProps), calculatedValues);
if (labelIndicator) {
const labelProps = childProps[eventKey].labels;
if (labelProps.calculatedLabelRadius > radius) {
childProps[eventKey].labelIndicators = getLabelIndicatorPropsForLineSegment(Object.assign({}, props, dataProps), calculatedValues, labelProps);
}
}
}
return childProps;
}, initialChildProps);
};
exports.getBaseProps = getBaseProps;