Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[charts] Refactor Tooltip customisation #15154

Merged
merged 30 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
1ebd04b
Move interaction state in a store for axis values
alexfauquette Oct 28, 2024
b22f8e1
WIP
alexfauquette Oct 29, 2024
9568f13
Merge remote-tracking branch 'upstream/master' into use-selector
alexfauquette Oct 30, 2024
814e4f7
fix error
alexfauquette Oct 30, 2024
1fbb282
UPdate tooltip organisation
alexfauquette Oct 30, 2024
da30115
typing and proptypes
alexfauquette Oct 30, 2024
9110c02
docs:api
alexfauquette Oct 30, 2024
32154bb
remove API pages
alexfauquette Oct 30, 2024
b031bf2
remove console.log
alexfauquette Oct 31, 2024
a69ff27
fix lint
alexfauquette Oct 31, 2024
0495bbd
fix tests
alexfauquette Oct 31, 2024
5a46b31
Merge remote-tracking branch 'upstream/master' into use-selector
alexfauquette Nov 12, 2024
1d6d261
disable eslint
alexfauquette Nov 12, 2024
542cee4
move useStore to internals
alexfauquette Nov 12, 2024
ae21ae1
scripts
alexfauquette Nov 12, 2024
4d28a07
new-tooltip-behavior
alexfauquette Nov 13, 2024
0e91863
docs:typescript
alexfauquette Nov 13, 2024
b4ac1db
missing-useclient
alexfauquette Nov 13, 2024
9af0972
JC feedback
alexfauquette Nov 13, 2024
930746f
fixes
alexfauquette Nov 13, 2024
a070ed6
revert perf suggestion
alexfauquette Nov 13, 2024
adec5bf
correct-fix
alexfauquette Nov 14, 2024
ff1d54b
ts-cleaning
alexfauquette Nov 14, 2024
3584fdf
Merge remote-tracking branch 'upstream/master' into use-selector
alexfauquette Nov 18, 2024
2e99533
typo fix
alexfauquette Nov 18, 2024
9a6c0c9
Merge remote-tracking branch 'upstream/master' into use-selector
alexfauquette Nov 18, 2024
5912bf0
fixes
alexfauquette Nov 18, 2024
aa493bc
scripts
alexfauquette Nov 19, 2024
57abfb6
factorise type def
alexfauquette Nov 19, 2024
57c0d26
Merge remote-tracking branch 'upstream/master' into use-selector
alexfauquette Nov 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
224 changes: 61 additions & 163 deletions docs/data/charts/tooltip/CustomAxisTooltip.js
Original file line number Diff line number Diff line change
@@ -1,184 +1,82 @@
import * as React from 'react';
import NoSsr from '@mui/material/NoSsr';
import Popper from '@mui/material/Popper';
import Paper from '@mui/material/Paper';
import Typography from '@mui/material/Typography';
import { useAxisTooltip } from '@mui/x-charts/ChartsTooltip';
import { useSvgRef } from '@mui/x-charts/hooks';

function usePointer() {
const svgRef = useSvgRef();
const popperRef = React.useRef(null);
const positionRef = React.useRef({ x: 0, y: 0 });

// Use a ref to avoid rerendering on every mousemove event.
const [pointer, setPointer] = React.useState({
isActive: false,
isMousePointer: false,
pointerHeight: 0,
});

React.useEffect(() => {
const element = svgRef.current;
if (element === null) {
return () => {};
}

const handleOut = (event) => {
if (event.pointerType !== 'mouse') {
setPointer((prev) => ({
...prev,
isActive: false,
}));
}
};

const handleEnter = (event) => {
setPointer({
isActive: true,
isMousePointer: event.pointerType === 'mouse',
pointerHeight: event.height,
});
};

const handleMove = (event) => {
positionRef.current = {
x: event.clientX,
y: event.clientY,
};
popperRef.current?.update();
};

element.addEventListener('pointerenter', handleEnter);
element.addEventListener('pointerup', handleOut);
element.addEventListener('pointermove', handleMove);

return () => {
element.removeEventListener('pointerenter', handleEnter);
element.removeEventListener('pointerup', handleOut);
element.removeEventListener('pointermove', handleMove);
};
}, [svgRef]);

return {
...pointer,
popperRef,
anchorEl: {
getBoundingClientRect: () => ({
x: positionRef.current.x,
y: positionRef.current.y,
top: positionRef.current.y,
left: positionRef.current.x,
right: positionRef.current.x,
bottom: positionRef.current.y,
width: 0,
height: 0,
toJSON: () => '',
}),
},
};
}
import { ChartsTooltipContainer, useAxisTooltip } from '@mui/x-charts/ChartsTooltip';

export function CustomAxisTooltip() {
const tooltipData = useAxisTooltip();
const { isActive, isMousePointer, pointerHeight, popperRef, anchorEl } =
usePointer();

if (!tooltipData || !isActive) {
if (!tooltipData) {
// No data to display
return null;
}

// The pointer type can be used to have different behavior based on pointer type.
// Adapt the tooltip offset to the size of the pointer.
const yOffset = isMousePointer ? 0 : 40 - pointerHeight;

return (
<NoSsr>
<Popper
<ChartsTooltipContainer trigger="axis">
<Paper
elevation={0}
sx={{
pointerEvents: 'none',
zIndex: (theme) => theme.zIndex.modal,
}}
open
placement={isMousePointer ? 'top-end' : 'top'}
anchorEl={anchorEl}
popperRef={popperRef}
modifiers={[
{
name: 'offset',
options: {
offset: [0, yOffset],
m: 1,
border: 'solid',
borderWidth: 2,
borderColor: 'divider',
table: { borderSpacing: 0 },
thead: {
td: {
px: 1.5,
py: 0.75,
borderBottom: 'solid',
borderWidth: 2,
borderColor: 'divider',
},
},
]}
>
<Paper
elevation={0}
sx={{
m: 1,
border: 'solid',
borderWidth: 2,
borderColor: 'divider',
table: { borderSpacing: 0 },
thead: {
tbody: {
'tr:first-child': { td: { paddingTop: 1.5 } },
'tr:last-child': { td: { paddingBottom: 1.5 } },
tr: {
'td:first-child': { paddingLeft: 1.5 },
'td:last-child': { paddingRight: 1.5 },
td: {
px: 1.5,
py: 0.75,
borderBottom: 'solid',
borderWidth: 2,
borderColor: 'divider',
},
},
tbody: {
'tr:first-child': { td: { paddingTop: 1.5 } },
'tr:last-child': { td: { paddingBottom: 1.5 } },
tr: {
'td:first-child': { paddingLeft: 1.5 },
'td:last-child': { paddingRight: 1.5 },
td: {
paddingRight: '7px',
paddingBottom: '10px',
},
paddingRight: '7px',
paddingBottom: '10px',
},
},
}}
>
<table>
<thead>
<tr>
<td colSpan={3}>
<Typography>{tooltipData.axisFormattedValue}</Typography>
},
}}
>
<table>
<thead>
<tr>
<td colSpan={3}>
<Typography>{tooltipData.axisFormattedValue}</Typography>
</td>
</tr>
</thead>
<tbody>
{tooltipData.seriesItems.map((seriesItem) => (
<tr key={seriesItem.seriesId}>
<td aria-label={`${seriesItem.formattedLabel}-series-color`}>
<div
style={{
width: 10,
height: 10,
borderRadius: 2,
backgroundColor: seriesItem.color,
}}
/>
</td>
<td>
<Typography fontWeight="light">
{seriesItem.formattedLabel}
</Typography>
</td>
<td>
<Typography>{seriesItem.formattedValue}</Typography>
</td>
</tr>
</thead>
<tbody>
{tooltipData.seriesItems.map((seriesItem) => (
<tr key={seriesItem.seriesId}>
<td aria-label={`${seriesItem.formattedLabel}-series-color`}>
<div
style={{
width: 10,
height: 10,
borderRadius: 2,
backgroundColor: seriesItem.color,
}}
/>
</td>
<td>
<Typography fontWeight="light">
{seriesItem.formattedLabel}
</Typography>
</td>
<td>
<Typography>{seriesItem.formattedValue}</Typography>
</td>
</tr>
))}
</tbody>
</table>
</Paper>
</Popper>
</NoSsr>
))}
</tbody>
</table>
</Paper>
</ChartsTooltipContainer>
);
}
Loading