Skip to content

Commit

Permalink
Reduce object allocations and return a set instead.
Browse files Browse the repository at this point in the history
  • Loading branch information
bernardobelchior committed Feb 19, 2025
1 parent c8ce95f commit bda19ce
Showing 1 changed file with 47 additions and 48 deletions.
95 changes: 47 additions & 48 deletions packages/x-charts/src/ChartsXAxis/ChartsXAxis.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,8 @@ const useUtilityClasses = (ownerState: ChartsXAxisProps & { theme: Theme }) => {
return composeClasses(slots, getAxisUtilityClass, classes);
};

type LabelExtraData = { skipLabel?: boolean };

function addLabelDimension(
/* Returns a set of indices of the tick labels that should be visible. */
function getVisibleLabels(
xTicks: TickItemType[],
{
tickLabelStyle: style,
Expand All @@ -49,7 +48,7 @@ function addLabelDimension(
isMounted: boolean;
isPointInside: (position: number) => boolean;
},
): (TickItemType & LabelExtraData)[] {
): Set<number> {
const getTickLabelSize = (tick: TickItemType) => {
if (!isMounted || tick.formattedValue === undefined) {
return { width: 0, height: 0 };
Expand All @@ -64,54 +63,52 @@ function addLabelDimension(
};

if (typeof tickLabelInterval === 'function') {
return xTicks.map((item, index) => {
const skipLabel = !tickLabelInterval(item.value, index);
const size = skipLabel ? { width: 0, height: 0 } : getTickLabelSize(item);

return {
...item,
...size,
skipLabel,
};
});
return new Set(
xTicks.filter((item, index) => tickLabelInterval(item.value, index)).map((_, i) => i),
);
}

// Filter label to avoid overlap
let previousTextLimit = 0;
const direction = reverse ? -1 : 1;

return xTicks.map((item, labelIndex) => {
const { offset, labelOffset } = item;
const textPosition = offset + labelOffset;

if (
labelIndex > 0 &&
direction * textPosition < direction * (previousTextLimit + tickLabelMinGap)
) {
return { ...item, skipLabel: true };
}

if (!isPointInside(textPosition)) {
return { ...item, skipLabel: true };
}

const { width, height } = getTickLabelSize(item);

const distance = getMinXTranslation(width, height, style?.angle);

const currentTextLimit = textPosition - (direction * distance) / 2;
if (
labelIndex > 0 &&
direction * currentTextLimit < direction * (previousTextLimit + tickLabelMinGap)
) {
// Except for the first label, we skip all label that overlap with the last accepted.
// Notice that the early return prevents `previousTextLimit` from being updated.
return { ...item, skipLabel: true };
}

previousTextLimit = textPosition + (direction * distance) / 2;
return item;
});
return new Set(
xTicks
.filter((item, labelIndex) => {
const { offset, labelOffset } = item;
const textPosition = offset + labelOffset;

if (
labelIndex > 0 &&
direction * textPosition < direction * (previousTextLimit + tickLabelMinGap)
) {
return false;
}

if (!isPointInside(textPosition)) {
return false;
}

/* Measuring text width is expensive, so we need to delay it as much as possible to improve performance. */
const { width, height } = getTickLabelSize(item);

const distance = getMinXTranslation(width, height, style?.angle);

const currentTextLimit = textPosition - (direction * distance) / 2;
if (
labelIndex > 0 &&
direction * currentTextLimit < direction * (previousTextLimit + tickLabelMinGap)
) {
// Except for the first label, we skip all label that overlap with the last accepted.
// Notice that the early return prevents `previousTextLimit` from being updated.
return false;
}

previousTextLimit = textPosition + (direction * distance) / 2;
return true;
})
.map((_, i) => i),
);
}

const XAxisRoot = styled(AxisRoot, {
Expand Down Expand Up @@ -206,7 +203,7 @@ function ChartsXAxis(inProps: ChartsXAxisProps) {
tickLabelPlacement,
});

const xTicksWithDimension = addLabelDimension(xTicks, {
const visibleLabels = getVisibleLabels(xTicks, {
tickLabelStyle: axisTickLabelProps.style,
tickLabelInterval,
reverse,
Expand Down Expand Up @@ -252,7 +249,7 @@ function ChartsXAxis(inProps: ChartsXAxisProps) {
<Line x1={left} x2={left + width} className={classes.line} {...slotProps?.axisLine} />
)}

{xTicksWithDimension.map(({ formattedValue, offset, labelOffset, skipLabel }, index) => {
{xTicks.map(({ formattedValue, offset, labelOffset }, index) => {
const xTickLabel = labelOffset ?? 0;
const yTickLabel = positionSign * (tickSize + 3);

Expand All @@ -261,6 +258,8 @@ function ChartsXAxis(inProps: ChartsXAxisProps) {
{ x: offset + xTickLabel, y: -1 },
{ direction: 'x' },
);
const skipLabel = !visibleLabels.has(index);

return (
<g key={index} transform={`translate(${offset}, 0)`} className={classes.tickContainer}>
{!disableTicks && showTick && (
Expand Down

0 comments on commit bda19ce

Please sign in to comment.