@awsui/components-react
Version:
On July 19th, 2022, we launched [Cloudscape Design System](https://cloudscape.design). Cloudscape is an evolution of AWS-UI. It consists of user interface guidelines, front-end components, design resources, and development tools for building intuitive, en
167 lines • 6.99 kB
JavaScript
import styles from './styles.css.js';
const minRadius = 30;
const paddingLabels = 44; // = 2 * (size-lineHeight-body-100)
const defaultPadding = 12; // = space-s
const smallPadding = 8; // = space-xs
export const minLabelLineAngularPadding = Math.PI / 20;
export const dimensionsBySize = {
small: {
innerRadius: 33,
outerRadius: 50,
innerLabelPadding: smallPadding,
padding: smallPadding,
paddingLabels,
},
medium: {
innerRadius: 66,
outerRadius: 100,
innerLabelPadding: defaultPadding,
padding: defaultPadding,
paddingLabels,
},
large: {
innerRadius: 93,
outerRadius: 140,
innerLabelPadding: defaultPadding,
padding: defaultPadding,
paddingLabels,
},
};
export const refreshDimensionsBySize = {
small: {
...dimensionsBySize.small,
innerRadius: 38,
cornerRadius: 3,
},
medium: {
...dimensionsBySize.medium,
innerRadius: 75,
cornerRadius: 4,
},
large: {
...dimensionsBySize.large,
innerRadius: 105,
cornerRadius: 5,
},
};
/**
* When `size` is a string ("small", "medium" or "large") the predefined pie chart element dimensions for classic and visual refresh are used.
* When `size` is a number the outer and inner radii are computed and the rest of the dimensions are taken from the closest predefined size.
*/
export function getDimensionsBySize({ size, hasLabels, visualRefresh, }) {
if (typeof size === 'string') {
const dimensions = visualRefresh ? refreshDimensionsBySize[size] : dimensionsBySize[size];
return { ...dimensions, size };
}
const sizeSpec = visualRefresh ? refreshDimensionsBySize : dimensionsBySize;
const getPixelSize = (d) => d.outerRadius * 2 + d.padding * 2 + (hasLabels ? d.paddingLabels : 0) * 2;
let matchedSize = 'small';
if (size > getPixelSize(sizeSpec.medium)) {
matchedSize = 'medium';
}
if (size > getPixelSize(sizeSpec.large)) {
matchedSize = 'large';
}
const padding = sizeSpec[matchedSize].padding;
const paddingLabels = hasLabels ? sizeSpec[matchedSize].paddingLabels : 0;
const radiiRatio = sizeSpec[matchedSize].outerRadius / sizeSpec[matchedSize].innerRadius;
const outerRadius = Math.max(minRadius, (size - 2 * paddingLabels - 2 * padding) / 2);
const innerRadius = outerRadius / radiiRatio;
return { ...sizeSpec[matchedSize], outerRadius, innerRadius, size: matchedSize };
}
export const defaultDetails = (i18n, i18nStrings) => (datum, dataSum) => [
{ key: i18n('i18nStrings.detailsValue', i18nStrings.detailsValue) || '', value: datum.value },
{
key: i18n('i18nStrings.detailsPercentage', i18nStrings.detailsPercentage) || '',
value: `${((datum.value * 100) / dataSum).toFixed(0)}%`,
},
];
/**
* Adjusts the position of the given label nodes to avoid visual overlapping.
* @param nodes List of label nodes of the entire chart (both left and right side)
* @param markers Markers array that was calculated in <Labels>, but we just need the `endY` values
* @param leftSide Boolean flag whether we are processing the left or right side of the chart labels
*/
export const balanceLabelNodes = (nodes, markers, leftSide, radius) => {
var _a;
const MARGIN = 10;
let previousBBox = null;
// When traversing the right side of labels, we start at the beginning of the array and go forwards.
// For the left side, we need to traverse backwards from the end, so that overlapping nodes are pushed down in the right order.
let i = leftSide ? nodes.length - 1 : 0;
while ((leftSide && i >= 0) || (!leftSide && i < nodes.length)) {
const node = nodes[i];
const x = parseFloat(node.getAttribute('data-x') || '0');
const y = parseFloat(node.getAttribute('data-y') || '0');
const box = {
x,
y,
height: node.getBoundingClientRect().height,
};
const marker = markers[i];
if (leftSide) {
i--;
}
else {
i++;
}
if (!previousBBox) {
previousBBox = box;
node.setAttribute('transform', '');
continue;
}
if ((!leftSide && box.x < 0) || (leftSide && box.x >= 0)) {
// We have reached a label that is on the other side of the chart, so we're done.
break;
}
node.setAttribute('transform', '');
// Calculate how much the current node is overlapping with the previous one.
const yOffset = previousBBox.y + previousBBox.height + MARGIN - box.y;
if (yOffset > 0) {
const xOffset = computeXOffset(box, yOffset, radius) * (leftSide ? -1 : 1);
// Move the label down.
node.setAttribute('transform', `translate(${xOffset} ${yOffset})`);
// Adjust the attached line accordingly.
const lineNode = (_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.querySelector(`.${styles['label-line']}`);
if (lineNode) {
const { endY, endX } = marker;
lineNode.setAttribute('y2', '' + (endY + yOffset));
lineNode.setAttribute('x2', '' + (endX + xOffset));
}
// Update the position accordingly to inform the next label
box.y += yOffset;
box.x += xOffset;
}
previousBBox = box;
}
};
const squareDistance = (edge) => Math.pow(edge[0], 2) + Math.pow(edge[1], 2);
const computeXOffset = (box, yOffset, radius) => {
const upperEdge = [box.x, box.y + yOffset];
const lowerEdge = [box.x, box.y + box.height + yOffset];
const closestEdge = squareDistance(upperEdge) < squareDistance(lowerEdge) ? upperEdge : lowerEdge;
if (squareDistance(closestEdge) < Math.pow(radius, 2)) {
return Math.sqrt(Math.pow(radius, 2) - Math.pow(closestEdge[1], 2)) - Math.abs(closestEdge[0]);
}
return 0;
};
export const computeSmartAngle = (startAngle, endAngle, optimize = false) => {
if (!optimize || endAngle - startAngle < 2 * minLabelLineAngularPadding) {
return (endAngle + startAngle) / 2;
}
const paddedStartAngle = startAngle + minLabelLineAngularPadding;
const paddedEndAngle = endAngle - minLabelLineAngularPadding;
if (paddedStartAngle < 0 && paddedEndAngle > 0) {
return 0;
}
if (paddedStartAngle < Math.PI && paddedEndAngle > Math.PI) {
return Math.PI;
}
const endAngleMinDistance = Math.min(paddedEndAngle, Math.abs(Math.PI - paddedEndAngle), 2 * Math.PI - paddedEndAngle);
const startAngleMinDistance = Math.min(paddedStartAngle, Math.abs(Math.PI - paddedStartAngle), 2 * Math.PI - paddedStartAngle);
if (endAngleMinDistance < startAngleMinDistance) {
return paddedEndAngle;
}
return paddedStartAngle;
};
//# sourceMappingURL=utils.js.map