Skip to content

Commit bda19ce

Browse files
Reduce object allocations and return a set instead.
1 parent c8ce95f commit bda19ce

File tree

1 file changed

+47
-48
lines changed

1 file changed

+47
-48
lines changed

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

+47-48
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,8 @@ const useUtilityClasses = (ownerState: ChartsXAxisProps & { theme: Theme }) => {
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
isMounted: boolean;
5049
isPointInside: (position: number) => boolean;
5150
},
52-
): (TickItemType & LabelExtraData)[] {
51+
): Set<number> {
5352
const getTickLabelSize = (tick: TickItemType) => {
5453
if (!isMounted || tick.formattedValue === undefined) {
5554
return { width: 0, height: 0 };
@@ -64,54 +63,52 @@ 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(
67+
xTicks.filter((item, index) => tickLabelInterval(item.value, index)).map((_, i) => i),
68+
);
7769
}
7870

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

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

117114
const XAxisRoot = styled(AxisRoot, {
@@ -206,7 +203,7 @@ function ChartsXAxis(inProps: ChartsXAxisProps) {
206203
tickLabelPlacement,
207204
});
208205

209-
const xTicksWithDimension = addLabelDimension(xTicks, {
206+
const visibleLabels = getVisibleLabels(xTicks, {
210207
tickLabelStyle: axisTickLabelProps.style,
211208
tickLabelInterval,
212209
reverse,
@@ -252,7 +249,7 @@ function ChartsXAxis(inProps: ChartsXAxisProps) {
252249
<Line x1={left} x2={left + width} className={classes.line} {...slotProps?.axisLine} />
253250
)}
254251

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

@@ -261,6 +258,8 @@ function ChartsXAxis(inProps: ChartsXAxisProps) {
261258
{ x: offset + xTickLabel, y: -1 },
262259
{ direction: 'x' },
263260
);
261+
const skipLabel = !visibleLabels.has(index);
262+
264263
return (
265264
<g key={index} transform={`translate(${offset}, 0)`} className={classes.tickContainer}>
266265
{!disableTicks && showTick && (

0 commit comments

Comments
 (0)