@mui/x-charts
Version:
The community edition of MUI X Charts components.
302 lines (300 loc) • 11.6 kB
JavaScript
'use client';
import _extends from "@babel/runtime/helpers/esm/extends";
import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
const _excluded = ["trigger", "classes", "children"];
import * as React from 'react';
import PropTypes from 'prop-types';
import HTMLElementType from '@mui/utils/HTMLElementType';
import useLazyRef from '@mui/utils/useLazyRef';
import { styled, useThemeProps } from '@mui/material/styles';
import Popper from '@mui/material/Popper';
import NoSsr from '@mui/material/NoSsr';
import { useSvgRef } from "../hooks/useSvgRef.js";
import { useIsFineMainPointer, usePointerType } from "./utils.js";
import { useUtilityClasses } from "./chartsTooltipClasses.js";
import { useSelector } from "../internals/store/useSelector.js";
import { useStore } from "../internals/store/useStore.js";
import { selectorChartsInteractionItemIsDefined } from "../internals/plugins/featurePlugins/useChartInteraction/index.js";
import { selectorChartsInteractionAxisTooltip } from "../internals/plugins/featurePlugins/useChartCartesianAxis/index.js";
import { selectorChartsInteractionPolarAxisTooltip } from "../internals/plugins/featurePlugins/useChartPolarAxis/useChartPolarInteraction.selectors.js";
import { useAxisSystem } from "../hooks/useAxisSystem.js";
import { jsx as _jsx } from "react/jsx-runtime";
const noAxis = () => false;
const ChartsTooltipRoot = styled(Popper, {
name: 'MuiChartsTooltip',
slot: 'Root'
})(({
theme
}) => ({
pointerEvents: 'none',
zIndex: theme.zIndex.modal
}));
/**
* Demos:
*
* - [ChartsTooltip](https://mui.com/x/react-charts/tooltip/)
*
* API:
*
* - [ChartsTooltip API](https://mui.com/x/api/charts/charts-tool-tip/)
*/
function ChartsTooltipContainer(inProps) {
const props = useThemeProps({
props: inProps,
name: 'MuiChartsTooltipContainer'
});
const {
trigger = 'axis',
classes: propClasses,
children
} = props,
other = _objectWithoutPropertiesLoose(props, _excluded);
const classes = useUtilityClasses(propClasses);
const svgRef = useSvgRef();
const pointerType = usePointerType();
const isFineMainPointer = useIsFineMainPointer();
const popperRef = React.useRef(null);
const positionRef = useLazyRef(() => ({
x: 0,
y: 0
}));
const axisSystem = useAxisSystem();
const store = useStore();
const isOpen = useSelector(store, trigger === 'axis' ? axisSystem === 'polar' && selectorChartsInteractionPolarAxisTooltip || axisSystem === 'cartesian' && selectorChartsInteractionAxisTooltip || noAxis : selectorChartsInteractionItemIsDefined);
React.useEffect(() => {
const element = svgRef.current;
if (element === null) {
return () => {};
}
const handlePointerEvent = event => {
// eslint-disable-next-line react-compiler/react-compiler
positionRef.current = {
x: event.clientX,
y: event.clientY
};
popperRef.current?.update();
};
element.addEventListener('pointerdown', handlePointerEvent);
element.addEventListener('pointermove', handlePointerEvent);
return () => {
element.removeEventListener('pointerdown', handlePointerEvent);
element.removeEventListener('pointermove', handlePointerEvent);
};
}, [svgRef, positionRef]);
const anchorEl = React.useMemo(() => ({
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: () => ''
})
}), [positionRef]);
const isMouse = pointerType?.pointerType === 'mouse' || isFineMainPointer;
const isTouch = pointerType?.pointerType === 'touch' || !isFineMainPointer;
const modifiers = React.useMemo(() => [{
name: 'offset',
options: {
offset: () => {
if (isTouch) {
return [0, 64];
}
// The popper offset: [skidding, distance]
return [0, 8];
}
}
}, ...(!isMouse ? [{
name: 'flip',
options: {
fallbackPlacements: ['top-end', 'top-start', 'bottom-end', 'bottom']
}
}] : []) // Keep default behavior
], [isMouse, isTouch]);
if (trigger === 'none') {
return null;
}
return /*#__PURE__*/_jsx(NoSsr, {
children: isOpen && /*#__PURE__*/_jsx(ChartsTooltipRoot, _extends({
className: classes?.root,
open: isOpen,
placement: isMouse ? 'right-start' : 'top',
popperRef: popperRef,
anchorEl: anchorEl,
modifiers: modifiers
}, other, {
children: children
}))
});
}
process.env.NODE_ENV !== "production" ? ChartsTooltipContainer.propTypes = {
// ----------------------------- Warning --------------------------------
// | These PropTypes are generated from the TypeScript type definitions |
// | To update them edit the TypeScript types and run "pnpm proptypes" |
// ----------------------------------------------------------------------
/**
* An HTML element, [virtualElement](https://popper.js.org/docs/v2/virtual-elements/),
* or a function that returns either.
* It's used to set the position of the popper.
* The return value will passed as the reference object of the Popper instance.
*/
anchorEl: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([HTMLElementType, PropTypes.object, PropTypes.func]),
/**
* Popper render function or node.
*/
children: PropTypes.node,
/**
* Override or extend the styles applied to the component.
*/
classes: PropTypes.object,
/**
* The component used for the root node.
* Either a string to use a HTML element or a component.
*/
component: PropTypes.elementType,
/**
* The components used for each slot inside the Popper.
* Either a string to use a HTML element or a component.
*
* @deprecated use the `slots` prop instead. This prop will be removed in a future major release. [How to migrate](/material-ui/migration/migrating-from-deprecated-apis/).
* @default {}
*/
components: PropTypes.shape({
Root: PropTypes.elementType
}),
/**
* The props used for each slot inside the Popper.
*
* @deprecated use the `slotProps` prop instead. This prop will be removed in a future major release. [How to migrate](/material-ui/migration/migrating-from-deprecated-apis/).
* @default {}
*/
componentsProps: PropTypes.shape({
root: PropTypes.oneOfType([PropTypes.func, PropTypes.object])
}),
/**
* An HTML element or function that returns one.
* The `container` will have the portal children appended to it.
*
* You can also provide a callback, which is called in a React layout effect.
* This lets you set the container from a ref, and also makes server-side rendering possible.
*
* By default, it uses the body of the top-level document object,
* so it's simply `document.body` most of the time.
*/
container: PropTypes.oneOfType([(props, propName) => {
if (props[propName] == null) {
return new Error(`Prop '${propName}' is required but wasn't specified`);
}
if (typeof props[propName] !== 'object' || props[propName].nodeType !== 1) {
return new Error(`Expected prop '${propName}' to be of type Element`);
}
return null;
}, PropTypes.func]),
/**
* The `children` will be under the DOM hierarchy of the parent component.
* @default false
*/
disablePortal: PropTypes.bool,
/**
* Always keep the children in the DOM.
* This prop can be useful in SEO situation or
* when you want to maximize the responsiveness of the Popper.
* @default false
*/
keepMounted: PropTypes.bool,
/**
* Popper.js is based on a "plugin-like" architecture,
* most of its features are fully encapsulated "modifiers".
*
* A modifier is a function that is called each time Popper.js needs to
* compute the position of the popper.
* For this reason, modifiers should be very performant to avoid bottlenecks.
* To learn how to create a modifier, [read the modifiers documentation](https://popper.js.org/docs/v2/modifiers/).
*/
modifiers: PropTypes.arrayOf(PropTypes.shape({
data: PropTypes.object,
effect: PropTypes.func,
enabled: PropTypes.bool,
fn: PropTypes.func,
name: PropTypes.any,
options: PropTypes.object,
phase: PropTypes.oneOf(['afterMain', 'afterRead', 'afterWrite', 'beforeMain', 'beforeRead', 'beforeWrite', 'main', 'read', 'write']),
requires: PropTypes.arrayOf(PropTypes.string),
requiresIfExists: PropTypes.arrayOf(PropTypes.string)
})),
/**
* If `true`, the component is shown.
*/
open: PropTypes.bool,
/**
* Popper placement.
* @default 'bottom'
*/
placement: PropTypes.oneOf(['auto-end', 'auto-start', 'auto', 'bottom-end', 'bottom-start', 'bottom', 'left-end', 'left-start', 'left', 'right-end', 'right-start', 'right', 'top-end', 'top-start', 'top']),
/**
* Options provided to the [`Popper.js`](https://popper.js.org/docs/v2/constructors/#options) instance.
* @default {}
*/
popperOptions: PropTypes.shape({
modifiers: PropTypes.array,
onFirstUpdate: PropTypes.func,
placement: PropTypes.oneOf(['auto-end', 'auto-start', 'auto', 'bottom-end', 'bottom-start', 'bottom', 'left-end', 'left-start', 'left', 'right-end', 'right-start', 'right', 'top-end', 'top-start', 'top']),
strategy: PropTypes.oneOf(['absolute', 'fixed'])
}),
/**
* A ref that points to the used popper instance.
*/
popperRef: PropTypes.oneOfType([PropTypes.func, PropTypes.shape({
current: PropTypes.shape({
destroy: PropTypes.func.isRequired,
forceUpdate: PropTypes.func.isRequired,
setOptions: PropTypes.func.isRequired,
state: PropTypes.shape({
attributes: PropTypes.object.isRequired,
elements: PropTypes.object.isRequired,
modifiersData: PropTypes.object.isRequired,
options: PropTypes.object.isRequired,
orderedModifiers: PropTypes.arrayOf(PropTypes.object).isRequired,
placement: PropTypes.oneOf(['auto-end', 'auto-start', 'auto', 'bottom-end', 'bottom-start', 'bottom', 'left-end', 'left-start', 'left', 'right-end', 'right-start', 'right', 'top-end', 'top-start', 'top']).isRequired,
rects: PropTypes.object.isRequired,
reset: PropTypes.bool.isRequired,
scrollParents: PropTypes.object.isRequired,
strategy: PropTypes.oneOf(['absolute', 'fixed']).isRequired,
styles: PropTypes.object.isRequired
}).isRequired,
update: PropTypes.func.isRequired
})
})]),
/**
* The props used for each slot inside the Popper.
* @default {}
*/
slotProps: PropTypes.object,
/**
* The components used for each slot inside the Popper.
* Either a string to use a HTML element or a component.
* @default {}
*/
slots: PropTypes.object,
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
sx: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), PropTypes.func, PropTypes.object]),
/**
* Help supporting a react-transition-group/Transition component.
* @default false
*/
transition: PropTypes.bool,
/**
* Select the kind of tooltip to display
* - 'item': Shows data about the item below the mouse.
* - 'axis': Shows values associated with the hovered x value
* - 'none': Does not display tooltip
* @default 'axis'
*/
trigger: PropTypes.oneOf(['axis', 'item', 'none'])
} : void 0;
export { ChartsTooltipContainer };