Skip to content

Commit 0c490da

Browse files
Reduce object allocations and return a set instead.
1 parent 880f8dd commit 0c490da

File tree

1 file changed

+45
-49
lines changed

1 file changed

+45
-49
lines changed

packages/x-charts/src/ChartsXAxis/ChartsXAxis.tsx

+45-49
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,8 @@ const useUtilityClasses = (ownerState: AxisConfig<any, any, ChartsXAxisProps>) =
3232
return composeClasses(slots, getAxisUtilityClass, classes);
3333
};
3434

35-
type LabelExtraData = { skipLabel?: boolean };
36-
37-
function addLabelDimension(
35+
/* Returns a set of indices of the tick labels that should be visible. */
36+
function getVisibleLabels(
3837
xTicks: TickItemType[],
3938
{
4039
tickLabelStyle: style,
@@ -49,7 +48,7 @@ function addLabelDimension(
4948
tickLabelMinGap: NonNullable<ChartsXAxisProps['tickLabelMinGap']>;
5049
isPointInside: (position: number) => boolean;
5150
},
52-
): (TickItemType & LabelExtraData)[] {
51+
): Set<TickItemType> {
5352
const getTickLabelSize = (tick: TickItemType) => {
5453
if (!isMounted || tick.formattedValue === undefined) {
5554
return { width: 0, height: 0 };
@@ -64,54 +63,48 @@ function addLabelDimension(
6463
};
6564

6665
if (typeof tickLabelInterval === 'function') {
67-
return xTicks.map((item, index) => {
68-
const skipLabel = !tickLabelInterval(item.value, index);
69-
const size = skipLabel ? { width: 0, height: 0 } : getTickLabelSize(item);
70-
71-
return {
72-
...item,
73-
...size,
74-
skipLabel,
75-
};
76-
});
66+
return new Set(xTicks.filter((item, index) => tickLabelInterval(item.value, index)));
7767
}
7868

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

83-
return xTicks.map((item, labelIndex) => {
84-
const { offset, labelOffset } = item;
85-
const textPosition = offset + labelOffset;
86-
87-
if (
88-
labelIndex > 0 &&
89-
direction * textPosition < direction * (previousTextLimit + tickLabelMinGap)
90-
) {
91-
return { ...item, skipLabel: true };
92-
}
93-
94-
if (!isPointInside(textPosition)) {
95-
return { ...item, skipLabel: true };
96-
}
97-
98-
const { width, height } = getTickLabelSize(item);
99-
100-
const distance = getMinXTranslation(width, height, style?.angle);
101-
102-
const currentTextLimit = textPosition - (direction * distance) / 2;
103-
if (
104-
labelIndex > 0 &&
105-
direction * currentTextLimit < direction * (previousTextLimit + tickLabelMinGap)
106-
) {
107-
// Except for the first label, we skip all label that overlap with the last accepted.
108-
// Notice that the early return prevents `previousTextLimit` from being updated.
109-
return { ...item, skipLabel: true };
110-
}
111-
112-
previousTextLimit = textPosition + (direction * distance) / 2;
113-
return item;
114-
});
73+
return new Set(
74+
xTicks.filter((item, labelIndex) => {
75+
const { offset, labelOffset } = item;
76+
const textPosition = offset + labelOffset;
77+
78+
if (
79+
labelIndex > 0 &&
80+
direction * textPosition < direction * (previousTextLimit + tickLabelMinGap)
81+
) {
82+
return false;
83+
}
84+
85+
if (!isPointInside(textPosition)) {
86+
return false;
87+
}
88+
89+
/* Measuring text width is expensive, so we need to delay it as much as possible to improve performance. */
90+
const { width, height } = getTickLabelSize(item);
91+
92+
const distance = getMinXTranslation(width, height, style?.angle);
93+
94+
const currentTextLimit = textPosition - (direction * distance) / 2;
95+
if (
96+
labelIndex > 0 &&
97+
direction * currentTextLimit < direction * (previousTextLimit + tickLabelMinGap)
98+
) {
99+
// Except for the first label, we skip all label that overlap with the last accepted.
100+
// Notice that the early return prevents `previousTextLimit` from being updated.
101+
return false;
102+
}
103+
104+
previousTextLimit = textPosition + (direction * distance) / 2;
105+
return true;
106+
}),
107+
);
115108
}
116109

117110
const XAxisRoot = styled(AxisRoot, {
@@ -208,7 +201,7 @@ function ChartsXAxis(inProps: ChartsXAxisProps) {
208201
tickLabelPlacement,
209202
});
210203

211-
const xTicksWithDimension = addLabelDimension(xTicks, {
204+
const visibleLabels = getVisibleLabels(xTicks, {
212205
tickLabelStyle: axisTickLabelProps.style,
213206
tickLabelInterval,
214207
tickLabelMinGap,
@@ -255,8 +248,9 @@ function ChartsXAxis(inProps: ChartsXAxisProps) {
255248
<Line x1={left} x2={left + width} className={classes.line} {...slotProps?.axisLine} />
256249
)}
257250

258-
{xTicksWithDimension.map(
259-
({ formattedValue, offset: tickOffset, labelOffset, skipLabel }, index) => {
251+
{xTicks.map(
252+
(item, index) => {
253+
const { formattedValue, offset: tickOffset, labelOffset } = item;
260254
const xTickLabel = labelOffset ?? 0;
261255
const yTickLabel = positionSign * (tickSize + 3);
262256

@@ -265,6 +259,8 @@ function ChartsXAxis(inProps: ChartsXAxisProps) {
265259
{ x: tickOffset + xTickLabel, y: -1 },
266260
{ direction: 'x' },
267261
);
262+
const skipLabel = !visibleLabels.has(item);
263+
268264
return (
269265
<g
270266
key={index}

0 commit comments

Comments
 (0)