Skip to content

Commit 5b49f62

Browse files
Apply same logic to x axis
1 parent ff5e6ba commit 5b49f62

File tree

5 files changed

+131
-134
lines changed

5 files changed

+131
-134
lines changed

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

Lines changed: 8 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
'use client';
22
import * as React from 'react';
33
import { ChartsXAxisProps, type AxisGroup } from '../models/axis';
4-
import { useDrawingArea } from '../hooks/useDrawingArea';
54
import { isBandScale } from '../internals/isBandScale';
65
import { useChartContext } from '../context/ChartProvider/useChartContext';
7-
import { TICK_LABEL_GAP, XAxisRoot } from './utilities';
6+
import { TICK_LABEL_GAP } from './utilities';
87
import { useTicksGrouped } from '../hooks/useTicksGrouped';
9-
import { useAxisProps } from './useAxisProps';
8+
import { useAxisTicksProps } from './useAxisTicksProps';
109

1110
const DEFAULT_GROUPING_CONFIG = {
1211
tickSize: 6,
@@ -29,24 +28,22 @@ const getGroupingConfig = (
2928
};
3029
};
3130

31+
interface ChartsGroupedXAxisProps extends ChartsXAxisProps {}
32+
3233
/**
3334
* @ignore - internal component.
3435
*/
35-
function ChartsGroupedXAxis(inProps: ChartsXAxisProps) {
36+
function ChartsGroupedXAxis(inProps: ChartsGroupedXAxisProps) {
3637
const {
3738
xScale,
3839
defaultizedProps,
3940
tickNumber,
4041
positionSign,
41-
skipAxisRendering,
4242
classes,
43-
Line,
4443
Tick,
4544
TickLabel,
46-
Label,
4745
axisTickLabelProps,
48-
axisLabelProps,
49-
} = useAxisProps(inProps);
46+
} = useAxisTicksProps(inProps);
5047

5148
if (!isBandScale(xScale)) {
5249
throw new Error(
@@ -55,32 +52,19 @@ function ChartsGroupedXAxis(inProps: ChartsXAxisProps) {
5552
}
5653

5754
const {
58-
position,
59-
disableLine,
6055
disableTicks,
61-
label,
6256
tickSize,
6357
valueFormatter,
6458
slotProps,
6559
tickInterval,
6660
tickPlacement,
6761
tickLabelPlacement,
68-
sx,
69-
offset,
70-
height: axisHeight,
7162
} = defaultizedProps;
7263

7364
const groups = (defaultizedProps as { groups: AxisGroup[] }).groups;
7465

75-
const drawingArea = useDrawingArea();
76-
const { left, top, width, height } = drawingArea;
7766
const { instance } = useChartContext();
7867

79-
const labelRefPoint = {
80-
x: left + width / 2,
81-
y: positionSign * axisHeight,
82-
};
83-
8468
const xTicks = useTicksGrouped({
8569
scale: xScale,
8670
tickNumber,
@@ -92,24 +76,8 @@ function ChartsGroupedXAxis(inProps: ChartsXAxisProps) {
9276
groups,
9377
});
9478

95-
// Skip axis rendering if no data is available
96-
// - The domain is an empty array for band/point scales.
97-
// - The domains contains Infinity for continuous scales.
98-
// - The position is set to 'none'.
99-
if (skipAxisRendering) {
100-
return null;
101-
}
102-
10379
return (
104-
<XAxisRoot
105-
transform={`translate(0, ${position === 'bottom' ? top + height + offset : top - offset})`}
106-
className={classes.root}
107-
sx={sx}
108-
>
109-
{!disableLine && (
110-
<Line x1={left} x2={left + width} className={classes.line} {...slotProps?.axisLine} />
111-
)}
112-
80+
<React.Fragment>
11381
{xTicks.map((item, index) => {
11482
const { offset: tickOffset, labelOffset } = item;
11583
const xTickLabel = labelOffset ?? 0;
@@ -149,13 +117,7 @@ function ChartsGroupedXAxis(inProps: ChartsXAxisProps) {
149117
</g>
150118
);
151119
})}
152-
153-
{label && (
154-
<g className={classes.label}>
155-
<Label {...labelRefPoint} {...axisLabelProps} text={label} />
156-
</g>
157-
)}
158-
</XAxisRoot>
120+
</React.Fragment>
159121
);
160122
}
161123

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

Lines changed: 17 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,43 @@
11
'use client';
22
import * as React from 'react';
3+
import { useRtl } from '@mui/system/RtlProvider';
34
import { useIsHydrated } from '../hooks/useIsHydrated';
4-
import { getStringSize } from '../internals/domUtils';
55
import { useTicks } from '../hooks/useTicks';
66
import { ChartsXAxisProps } from '../models/axis';
77
import { useMounted } from '../hooks/useMounted';
88
import { useDrawingArea } from '../hooks/useDrawingArea';
99
import { useChartContext } from '../context/ChartProvider/useChartContext';
1010
import { shortenLabels } from './shortenLabels';
1111
import { getVisibleLabels } from './getVisibleLabels';
12-
import { AXIS_LABEL_TICK_LABEL_GAP, TICK_LABEL_GAP, XAxisRoot } from './utilities';
13-
import { useAxisProps } from './useAxisProps';
12+
import { AXIS_LABEL_TICK_LABEL_GAP, TICK_LABEL_GAP } from './utilities';
13+
import { useAxisTicksProps } from './useAxisTicksProps';
14+
15+
interface ChartsSingleXAxisProps extends ChartsXAxisProps {
16+
axisLabelHeight: number;
17+
}
1418

1519
/**
1620
* @ignore - internal component.
1721
*/
18-
function ChartsSingleXAxis(inProps: ChartsXAxisProps) {
22+
function ChartsSingleXAxis(inProps: ChartsSingleXAxisProps) {
23+
const { axisLabelHeight } = inProps;
1924
const {
2025
xScale,
2126
defaultizedProps,
2227
tickNumber,
2328
positionSign,
24-
skipAxisRendering,
2529
classes,
26-
Line,
2730
Tick,
2831
TickLabel,
29-
Label,
3032
axisTickLabelProps,
31-
axisLabelProps,
3233
reverse,
33-
isRtl,
34-
} = useAxisProps(inProps);
34+
} = useAxisTicksProps(inProps);
3535

36+
const isRtl = useRtl();
3637
const isMounted = useMounted();
3738

3839
const {
39-
position,
40-
disableLine,
4140
disableTicks,
42-
label,
4341
tickSize: tickSizeProp,
4442
valueFormatter,
4543
slotProps,
@@ -48,13 +46,10 @@ function ChartsSingleXAxis(inProps: ChartsXAxisProps) {
4846
tickPlacement,
4947
tickLabelPlacement,
5048
tickLabelMinGap,
51-
sx,
52-
offset,
5349
height: axisHeight,
5450
} = defaultizedProps;
5551

5652
const drawingArea = useDrawingArea();
57-
const { left, top, width, height } = drawingArea;
5853
const { instance } = useChartContext();
5954
const isHydrated = useIsHydrated();
6055

@@ -79,24 +74,13 @@ function ChartsSingleXAxis(inProps: ChartsXAxisProps) {
7974
isXInside: instance.isXInside,
8075
});
8176

82-
// Skip axis rendering if no data is available
83-
// - The domain is an empty array for band/point scales.
84-
// - The domains contains Infinity for continuous scales.
85-
// - The position is set to 'none'.
86-
if (skipAxisRendering) {
87-
return null;
88-
}
89-
90-
const labelHeight = label ? getStringSize(label, axisLabelProps.style).height : 0;
91-
const labelRefPoint = {
92-
x: left + width / 2,
93-
y: positionSign * axisHeight,
94-
};
95-
9677
/* If there's an axis title, the tick labels have less space to render */
9778
const tickLabelsMaxHeight = Math.max(
9879
0,
99-
axisHeight - (label ? labelHeight + AXIS_LABEL_TICK_LABEL_GAP : 0) - tickSize - TICK_LABEL_GAP,
80+
axisHeight -
81+
(axisLabelHeight > 0 ? axisLabelHeight + AXIS_LABEL_TICK_LABEL_GAP : 0) -
82+
tickSize -
83+
TICK_LABEL_GAP,
10084
);
10185

10286
const tickLabels = isHydrated
@@ -110,15 +94,7 @@ function ChartsSingleXAxis(inProps: ChartsXAxisProps) {
11094
: new Map(Array.from(visibleLabels).map((item) => [item, item.formattedValue]));
11195

11296
return (
113-
<XAxisRoot
114-
transform={`translate(0, ${position === 'bottom' ? top + height + offset : top - offset})`}
115-
className={classes.root}
116-
sx={sx}
117-
>
118-
{!disableLine && (
119-
<Line x1={left} x2={left + width} className={classes.line} {...slotProps?.axisLine} />
120-
)}
121-
97+
<React.Fragment>
12298
{xTicks.map((item, index) => {
12399
const { offset: tickOffset, labelOffset } = item;
124100
const xTickLabel = labelOffset ?? 0;
@@ -154,13 +130,7 @@ function ChartsSingleXAxis(inProps: ChartsXAxisProps) {
154130
</g>
155131
);
156132
})}
157-
158-
{label && (
159-
<g className={classes.label}>
160-
<Label {...labelRefPoint} {...axisLabelProps} text={label} />
161-
</g>
162-
)}
163-
</XAxisRoot>
133+
</React.Fragment>
164134
);
165135
}
166136

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

Lines changed: 100 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
'use client';
22
import * as React from 'react';
33
import PropTypes from 'prop-types';
4+
import useSlotProps from '@mui/utils/useSlotProps';
5+
import { useTheme, useThemeProps } from '@mui/material/styles';
46
import { ChartsXAxisProps } from '../models/axis';
57
import { useXAxes } from '../hooks/useAxis';
68
import { ChartsSingleXAxis } from './ChartsSingleXAxis';
79
import { ChartsGroupedXAxis } from './ChartsGroupedXAxis';
10+
import { ChartsText, ChartsTextProps } from '../ChartsText';
11+
import { isBandScale } from '../internals/isBandScale';
12+
import { isInfinity } from '../internals/isInfinity';
13+
import { defaultProps, useUtilityClasses, XAxisRoot } from './utilities';
14+
import { useDrawingArea } from '../hooks';
15+
import { getStringSize } from '../internals/domUtils';
816

917
/**
1018
* Demos:
@@ -17,13 +25,101 @@ import { ChartsGroupedXAxis } from './ChartsGroupedXAxis';
1725
*/
1826
function ChartsXAxis(inProps: ChartsXAxisProps) {
1927
const { xAxis, xAxisIds } = useXAxes();
20-
2128
const axis = xAxis[inProps.axisId ?? xAxisIds[0]];
22-
if ('groups' in axis && Array.isArray(axis.groups)) {
23-
return <ChartsGroupedXAxis {...inProps} />;
29+
const { scale: xScale, tickNumber, reverse, ...settings } = axis;
30+
31+
const themedProps = useThemeProps({ props: { ...settings, ...inProps }, name: 'MuiChartsXAxis' });
32+
33+
const defaultizedProps = {
34+
...defaultProps,
35+
...themedProps,
36+
};
37+
38+
const {
39+
position,
40+
labelStyle,
41+
offset,
42+
slots,
43+
slotProps,
44+
sx,
45+
disableLine,
46+
label,
47+
height: axisHeight,
48+
} = defaultizedProps;
49+
50+
const theme = useTheme();
51+
const classes = useUtilityClasses(defaultizedProps);
52+
const drawingArea = useDrawingArea();
53+
const { left, top, width, height } = drawingArea;
54+
55+
const positionSign = position === 'bottom' ? 1 : -1;
56+
57+
const Line = slots?.axisLine ?? 'line';
58+
const Label = slots?.axisLabel ?? ChartsText;
59+
60+
const axisLabelProps = useSlotProps({
61+
elementType: Label,
62+
externalSlotProps: slotProps?.axisLabel,
63+
additionalProps: {
64+
style: {
65+
...theme.typography.body1,
66+
lineHeight: 1,
67+
fontSize: 14,
68+
textAnchor: 'middle',
69+
dominantBaseline: position === 'bottom' ? 'text-after-edge' : 'text-before-edge',
70+
...labelStyle,
71+
},
72+
} as Partial<ChartsTextProps>,
73+
ownerState: {},
74+
});
75+
76+
const domain = xScale.domain();
77+
const isScaleBand = isBandScale(xScale);
78+
79+
// Skip axis rendering if no data is available
80+
// - The domain is an empty array for band/point scales.
81+
// - The domains contains Infinity for continuous scales.
82+
// - The position is set to 'none'.
83+
const skipAxisRendering =
84+
(isScaleBand && domain.length === 0) ||
85+
(!isScaleBand && domain.some(isInfinity)) ||
86+
position === 'none';
87+
88+
if (skipAxisRendering) {
89+
return null;
2490
}
2591

26-
return <ChartsSingleXAxis {...inProps} />;
92+
const labelHeight = label ? getStringSize(label, axisLabelProps.style).height : 0;
93+
94+
const children =
95+
'groups' in axis && Array.isArray(axis.groups) ? (
96+
<ChartsGroupedXAxis {...inProps} />
97+
) : (
98+
<ChartsSingleXAxis {...inProps} axisLabelHeight={labelHeight} />
99+
);
100+
101+
const labelRefPoint = {
102+
x: left + width / 2,
103+
y: positionSign * axisHeight,
104+
};
105+
106+
return (
107+
<XAxisRoot
108+
transform={`translate(0, ${position === 'bottom' ? top + height + offset : top - offset})`}
109+
className={classes.root}
110+
sx={sx}
111+
>
112+
{!disableLine && (
113+
<Line x1={left} x2={left + width} className={classes.line} {...slotProps?.axisLine} />
114+
)}
115+
{children}
116+
{label && (
117+
<g className={classes.label}>
118+
<Label {...labelRefPoint} {...axisLabelProps} text={label} />
119+
</g>
120+
)}
121+
</XAxisRoot>
122+
);
27123
}
28124

29125
ChartsXAxis.propTypes = {

0 commit comments

Comments
 (0)