UNPKG

@grafana/ui

Version:
107 lines (104 loc) 3.88 kB
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