diff --git a/docs/data/charts/tooltip/CustomAxisTooltip.tsx b/docs/data/charts/tooltip/CustomAxisTooltip.tsx index 010af780809fe..0d0693111c49c 100644 --- a/docs/data/charts/tooltip/CustomAxisTooltip.tsx +++ b/docs/data/charts/tooltip/CustomAxisTooltip.tsx @@ -1,190 +1,82 @@ import * as React from 'react'; -import NoSsr from '@mui/material/NoSsr'; -import Popper, { PopperProps } 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'; - -type PointerState = { - isActive: boolean; - isMousePointer: boolean; - pointerHeight: number; -}; - -function usePointer(): PointerState & Pick { - const svgRef = useSvgRef(); - const popperRef: PopperProps['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: PointerEvent) => { - if (event.pointerType !== 'mouse') { - setPointer((prev) => ({ - ...prev, - isActive: false, - })); - } - }; - - const handleEnter = (event: PointerEvent) => { - setPointer({ - isActive: true, - isMousePointer: event.pointerType === 'mouse', - pointerHeight: event.height, - }); - }; - - const handleMove = (event: PointerEvent) => { - 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 ( - - + 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', }, }, - ]} - > - - - - -
- {tooltipData.axisFormattedValue} + }, + }} + > + + + + + + + + {tooltipData.seriesItems.map((seriesItem) => ( + + + + - - - {tooltipData.seriesItems.map((seriesItem) => ( - - - - - - ))} - -
+ {tooltipData.axisFormattedValue} +
+
+
+ + {seriesItem.formattedLabel} + + + {seriesItem.formattedValue}
-
-
- - {seriesItem.formattedLabel} - - - {seriesItem.formattedValue} -
- - - + ))} + +
+
+ ); } diff --git a/docs/data/charts/tooltip/CustomItemTooltip.tsx b/docs/data/charts/tooltip/CustomItemTooltip.tsx index 077243411c5d3..99ceb0dca0ffa 100644 --- a/docs/data/charts/tooltip/CustomItemTooltip.tsx +++ b/docs/data/charts/tooltip/CustomItemTooltip.tsx @@ -1,150 +1,44 @@ import * as React from 'react'; -import NoSsr from '@mui/material/NoSsr'; -import Popper, { PopperProps } from '@mui/material/Popper'; import Paper from '@mui/material/Paper'; import Stack from '@mui/material/Stack'; import Typography from '@mui/material/Typography'; -import { useItemTooltip } from '@mui/x-charts/ChartsTooltip'; -import { useSvgRef } from '@mui/x-charts/hooks'; - -type PointerState = { - isActive: boolean; - isMousePointer: boolean; - pointerHeight: number; -}; - -function usePointer(): PointerState & Pick { - const svgRef = useSvgRef(); - const popperRef: PopperProps['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: PointerEvent) => { - if (event.pointerType !== 'mouse') { - setPointer((prev) => ({ - ...prev, - isActive: false, - })); - } - }; - - const handleEnter = (event: PointerEvent) => { - setPointer({ - isActive: true, - isMousePointer: event.pointerType === 'mouse', - pointerHeight: event.height, - }); - }; - - const handleMove = (event: PointerEvent) => { - 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, useItemTooltip } from '@mui/x-charts/ChartsTooltip'; export function CustomItemTooltip() { const tooltipData = useItemTooltip(); - const { isActive, isMousePointer, pointerHeight, popperRef, anchorEl } = - usePointer(); - if (!tooltipData || !isActive) { + if (!tooltipData) { // No data to display return null; } - // Adapt the tooltip offset to the size of the pointer. - const yOffset = isMousePointer ? 0 : 40 - pointerHeight; - return ( - - + theme.zIndex.modal, + m: 1, + p: 1.5, + border: 'solid', + borderWidth: 2, + borderColor: 'divider', }} - open - placement={isMousePointer ? 'top-end' : 'top'} - anchorEl={anchorEl} - popperRef={popperRef} - modifiers={[ - { - name: 'offset', - options: { - offset: [0, yOffset], - }, - }, - ]} > - - -
- - {tooltipData.label} - - {tooltipData.formattedValue} - - - - + +
+ + {tooltipData.label} + + {tooltipData.formattedValue} + + + ); } diff --git a/docs/data/charts/tooltip/Interaction.tsx b/docs/data/charts/tooltip/Interaction.tsx index cacdca34fd5a1..5443660027f8e 100644 --- a/docs/data/charts/tooltip/Interaction.tsx +++ b/docs/data/charts/tooltip/Interaction.tsx @@ -21,8 +21,8 @@ const barChartsParams = { export default function Interaction() { return ( - - + + ); } diff --git a/docs/data/charts/tooltip/tooltip.md b/docs/data/charts/tooltip/tooltip.md index f53171fca6e52..b2e98b07d3c50 100644 --- a/docs/data/charts/tooltip/tooltip.md +++ b/docs/data/charts/tooltip/tooltip.md @@ -1,15 +1,15 @@ --- title: Charts - Tooltip productId: x-charts -components: ChartsTooltip, DefaultChartsAxisTooltipContent, DefaultChartsItemTooltipContent +components: ChartsTooltip --- # Charts - Tooltip

Tooltip provides extra data on charts item.

-In all charts components, you can pass props to the tooltip by using `tooltip={{...}}`. -If you are using composition, you can add the `` component and pass props directly. +In all charts components, the tooltip is accessible via the slot `tooltip`. +If you are using composition, you can use the `` component. ## Tooltip trigger @@ -90,27 +90,41 @@ It removes the header showing the x-axis value from the tooltip. ### Overriding content -To modify the tooltip content, use `slots.itemContent` or `slots.axisContent`. -The first one is rendered when tooltip trigger is set to `"item"`. -The second one when trigger is set to `"axis"`. +To override tooltip content, provide a custom component to `slots.tooltip`. +Some helper are provided, such as: + +- `` which provide a tooltip with built-in open and position management. +- `useItemTooltip()` which provides all basic information associated to the current item. +- `useAxisTooltip()` which provides all basic information associated to the current axis. + +Here is the basic scheme to follow. +Examples about helpers are provided in the composition section. ```jsx -// With single component +import { ChartsTooltipContainer } from '@mui/x-charts/ChartsTooltip'; + +function CustomItemTooltip() { + const tooltipData = useItemTooltip(); + + if (!tooltipData) { // No data to display + return null; + } + + return ( + + {/** Your custom content **/} + + ) +} + // With composition // ... - + ``` diff --git a/packages/x-charts/src/ChartsTooltip/ChartsAxisTooltipContent.tsx b/packages/x-charts/src/ChartsTooltip/ChartsAxisTooltipContent.tsx index 3f8f23fcdc8b2..b881d3f3eab0d 100644 --- a/packages/x-charts/src/ChartsTooltip/ChartsAxisTooltipContent.tsx +++ b/packages/x-charts/src/ChartsTooltip/ChartsAxisTooltipContent.tsx @@ -21,9 +21,6 @@ export type ChartsAxisContentProps = { sx?: SxProps; }; -/** - * @ignore - internal component. - */ function ChartsAxisTooltipContent(props: { sx?: SxProps; classes: ChartsAxisContentProps['classes']; diff --git a/packages/x-charts/src/ChartsTooltip/ChartsItemTooltipContent.tsx b/packages/x-charts/src/ChartsTooltip/ChartsItemTooltipContent.tsx index d8ff37e413e9e..aab744661ada0 100644 --- a/packages/x-charts/src/ChartsTooltip/ChartsItemTooltipContent.tsx +++ b/packages/x-charts/src/ChartsTooltip/ChartsItemTooltipContent.tsx @@ -16,9 +16,6 @@ export interface ChartsItemTooltipContentProps { classes: ChartsTooltipClasses; } -/** - * @ignore - internal component. - */ function ChartsItemTooltipContent(props: ChartsItemTooltipContentProps) { const { classes, sx } = props; const tooltipData = useItemTooltip();