@grafana/ui
Version:
Grafana Components Library
107 lines (104 loc) • 3.88 kB
JavaScript
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
import { offset, arrow, useFloating, autoUpdate, useHover, safePolygon, useFocus, useDismiss, useInteractions, FloatingArrow } from '@floating-ui/react';
import { forwardRef, useRef, useState, useId, useCallback, cloneElement, isValidElement } from 'react';
import { selectors } from '@grafana/e2e-selectors';
import { useStyles2 } from '../../themes/ThemeContext.mjs';
import { getPositioningMiddleware } from '../../utils/floating.mjs';
import { getPlacement, buildTooltipTheme } from '../../utils/tooltipUtils.mjs';
import { Portal } from '../Portal/Portal.mjs';
"use strict";
const Tooltip = forwardRef(
({ children, theme, interactive, show, placement, content }, forwardedRef) => {
const arrowRef = useRef(null);
const [controlledVisible, setControlledVisible] = useState(show);
const isOpen = show != null ? show : controlledVisible;
const floatingUIPlacement = getPlacement(placement);
const middleware = [
offset(8),
...getPositioningMiddleware(floatingUIPlacement),
arrow({
element: arrowRef
})
];
const { context, refs, floatingStyles } = useFloating({
open: isOpen,
placement: floatingUIPlacement,
onOpenChange: setControlledVisible,
middleware,
whileElementsMounted: autoUpdate
});
const tooltipId = useId();
const hover = useHover(context, {
handleClose: interactive ? safePolygon() : void 0,
move: false
});
const focus = useFocus(context);
const dismiss = useDismiss(context);
const { getReferenceProps, getFloatingProps } = useInteractions([dismiss, hover, focus]);
const contentIsFunction = typeof content === "function";
const styles = useStyles2(getStyles);
const style = styles[theme != null ? theme : "info"];
const handleRef = useCallback(
(ref) => {
refs.setReference(ref);
if (typeof forwardedRef === "function") {
forwardedRef(ref);
} else if (forwardedRef) {
forwardedRef.current = ref;
}
},
[forwardedRef, refs]
);
const childHasMatchingAriaLabel = "aria-label" in children.props && children.props["aria-label"] === content;
return /* @__PURE__ */ jsxs(Fragment, { children: [
cloneElement(children, {
ref: handleRef,
tabIndex: 0,
// tooltip trigger should be keyboard focusable
"aria-describedby": !childHasMatchingAriaLabel && isOpen ? tooltipId : void 0,
...getReferenceProps()
}),
isOpen && /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsxs("div", { ref: refs.setFloating, style: floatingStyles, ...getFloatingProps(), children: [
/* @__PURE__ */ jsx(FloatingArrow, { className: style.arrow, ref: arrowRef, context }),
/* @__PURE__ */ jsxs(
"div",
{
"data-testid": selectors.components.Tooltip.container,
id: tooltipId,
role: "tooltip",
className: style.container,
children: [
typeof content === "string" && content,
isValidElement(content) && cloneElement(content),
contentIsFunction && content({})
]
}
)
] }) })
] });
}
);
Tooltip.displayName = "Tooltip";
const getStyles = (theme) => {
const info = buildTooltipTheme(
theme,
theme.components.tooltip.background,
theme.components.tooltip.background,
theme.components.tooltip.text,
{ topBottom: 0.5, rightLeft: 1 }
);
const error = buildTooltipTheme(
theme,
theme.colors.error.main,
theme.colors.error.main,
theme.colors.error.contrastText,
{ topBottom: 0.5, rightLeft: 1 }
);
return {
info,
["info-alt"]: info,
error
};
};
export { Tooltip, getStyles };
//# sourceMappingURL=Tooltip.mjs.map