in src/axis/axis.ts [321:472]
export function getTickLabelMargins(
viewport: powerbi.IViewport,
yMarginLimit: number,
textWidthMeasurer: ITextAsSVGMeasurer,
textHeightMeasurer: ITextAsSVGMeasurer,
axes: CartesianAxisProperties,
bottomMarginLimit: number,
properties: TextProperties,
scrollbarVisible?: boolean,
showOnRight?: boolean,
renderXAxis?: boolean,
renderY1Axis?: boolean,
renderY2Axis?: boolean): TickLabelMargins /*IMargin - can't update this because custom visuals use it*/ {
let xAxisProperties: IAxisProperties = axes.x;
let y1AxisProperties: IAxisProperties = axes.y1;
let y2AxisProperties: IAxisProperties = axes.y2;
let xLabels = xAxisProperties.values;
let y1Labels = y1AxisProperties.values;
let leftOverflow = 0;
let rightOverflow = 0;
let maxWidthY1 = 0;
let maxWidthY2 = 0;
let xMax = 0; // bottom margin
let ordinalLabelOffset = xAxisProperties.categoryThickness ? xAxisProperties.categoryThickness / 2 : 0;
let scaleIsOrdinal = isOrdinalScale(xAxisProperties.scale);
let hasHierarchy = !arrayIsEmpty(axes.xStack);
let xLabelOuterPadding = 0;
if (xAxisProperties.outerPadding !== undefined) {
xLabelOuterPadding = xAxisProperties.outerPadding;
}
else if (xAxisProperties.xLabelMaxWidth !== undefined) {
xLabelOuterPadding = Math.max(0, (viewport.width - xAxisProperties.xLabelMaxWidth * xLabels.length) / 2);
}
let textHeight: number;
let rotation;
if (scrollbarVisible || hasHierarchy)
rotation = LabelLayoutStrategy.DefaultRotationWithScrollbar;
else
rotation = LabelLayoutStrategy.DefaultRotation;
if (renderY1Axis) {
for (let i = 0, len = y1Labels.length; i < len; i++) {
properties.text = y1Labels[i];
maxWidthY1 = Math.max(maxWidthY1, textWidthMeasurer(properties));
}
}
if (y2AxisProperties && renderY2Axis) {
let y2Labels = y2AxisProperties.values;
for (let i = 0, len = y2Labels.length; i < len; i++) {
properties.text = y2Labels[i];
maxWidthY2 = Math.max(maxWidthY2, textWidthMeasurer(properties));
}
}
textHeight = textHeightMeasurer(properties);
let maxNumLines = Math.floor(bottomMarginLimit / textHeight);
let xScale = xAxisProperties.scale;
let xDomain = xScale.domain();
if (renderXAxis && xLabels.length > 0) {
for (let i = 0, len = xLabels.length; i < len; i++) {
// find the max height of the x-labels, perhaps rotated or wrapped
let height: number;
properties.text = xLabels[i];
let width = textWidthMeasurer(properties);
if (xAxisProperties.willLabelsWordBreak) {
// Split label and count rows
let wordBreaks = wordBreaker.splitByWidth(properties.text, properties, textWidthMeasurer, xAxisProperties.xLabelMaxWidth, maxNumLines);
height = wordBreaks.length * textHeight;
// word wrapping will truncate at xLabelMaxWidth
width = xAxisProperties.xLabelMaxWidth;
}
else if (!xAxisProperties.willLabelsFit && scaleIsOrdinal) {
height = width * rotation.sine;
width = width * rotation.cosine;
}
else {
height = TextHeightConstant;
}
// calculate left and right overflow due to wide X labels
// (Note: no right overflow when rotated)
if (i === 0) {
if (scaleIsOrdinal) {
if (!xAxisProperties.willLabelsFit /*rotated text*/)
leftOverflow = width - ordinalLabelOffset - xLabelOuterPadding;
else
leftOverflow = (width / 2) - ordinalLabelOffset - xLabelOuterPadding;
leftOverflow = Math.max(leftOverflow, 0);
}
else if (xDomain.length > 1) {
// Scalar - do some math
let xPos = xScale(xDomain[0]);
// xPos already incorporates xLabelOuterPadding, don't subtract it twice
leftOverflow = (width / 2) - xPos;
leftOverflow = Math.max(leftOverflow, 0);
}
} else if (i === len - 1) {
if (scaleIsOrdinal) {
// if we are rotating text (!willLabelsFit) there won't be any right overflow
if (xAxisProperties.willLabelsFit || xAxisProperties.willLabelsWordBreak) {
// assume this label is placed near the edge
rightOverflow = (width / 2) - ordinalLabelOffset - xLabelOuterPadding;
rightOverflow = Math.max(rightOverflow, 0);
}
}
else if (xDomain.length > 1) {
// Scalar - do some math
let xPos = xScale(xDomain[1]);
// xPos already incorporates xLabelOuterPadding, don't subtract it twice
rightOverflow = (width / 2) - (viewport.width - xPos);
rightOverflow = Math.max(rightOverflow, 0);
}
}
xMax = Math.max(xMax, height);
}
// trim any actual overflow to the limit
leftOverflow = Math.min(leftOverflow, XLabelMaxAllowedOverflow);
rightOverflow = Math.min(rightOverflow, XLabelMaxAllowedOverflow);
}
let rightMargin = 0,
leftMargin = 0,
bottomMargin = Math.min(Math.ceil(xMax), bottomMarginLimit);
if (showOnRight) {
leftMargin = Math.min(Math.max(leftOverflow, maxWidthY2), yMarginLimit);
rightMargin = Math.min(Math.max(rightOverflow, maxWidthY1), yMarginLimit);
}
else {
leftMargin = Math.min(Math.max(leftOverflow, maxWidthY1), yMarginLimit);
rightMargin = Math.min(Math.max(rightOverflow, maxWidthY2), yMarginLimit);
}
if (hasHierarchy) {
bottomMargin += (textHeight + stackedAxisPadding) * (axes.xStack.length - 1);
}
return {
xMax: Math.ceil(bottomMargin),
yLeft: Math.ceil(leftMargin),
yRight: Math.ceil(rightMargin),
stackHeight: textHeight + stackedAxisPadding,
};
}