UNPKG

@equinor/eds-core-react

Version:

The React implementation of the Equinor Design System

150 lines (147 loc) 5.02 kB
import { forwardRef, useRef, useState, useMemo, useEffect, cloneElement } from 'react'; import { createPortal } from 'react-dom'; import styled from 'styled-components'; import { typographyTemplate, spacingsTemplate, bordersTemplate, mergeRefs } from '@equinor/eds-utils'; import { tooltip } from './Tooltip.tokens.js'; import { useFloating, offset, flip, shift, arrow, autoUpdate, useInteractions, useHover, useFocus, useRole, useDismiss } from '@floating-ui/react'; import { jsxs, jsx, Fragment } from 'react/jsx-runtime'; import { useEds } from '../EdsProvider/eds.context.js'; const StyledTooltip = styled('div').withConfig({ shouldForwardProp: () => true //workaround to avoid warning until popover gets added to react types }).withConfig({ displayName: "Tooltip__StyledTooltip", componentId: "sc-m2im2p-0" })(["inset:unset;border:0;overflow:visible;", " ", " ", " background:", ";white-space:nowrap;&::before{content:'; Has tooltip: ';clip-path:inset(50%);height:1px;width:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;}&::backdrop{background-color:transparent;}"], typographyTemplate(tooltip.typography), spacingsTemplate(tooltip.spacings), bordersTemplate(tooltip.border), tooltip.background); const ArrowWrapper = styled.div.withConfig({ displayName: "Tooltip__ArrowWrapper", componentId: "sc-m2im2p-1" })(["position:absolute;width:", ";height:", ";z-index:-1;"], tooltip.entities.arrow.width, tooltip.entities.arrow.height); const TooltipArrow = styled.svg.withConfig({ displayName: "Tooltip__TooltipArrow", componentId: "sc-m2im2p-2" })(["width:", ";height:", ";position:absolute;fill:", ";"], tooltip.entities.arrow.width, tooltip.entities.arrow.height, tooltip.background); const Tooltip = /*#__PURE__*/forwardRef(function Tooltip({ title, placement = 'bottom', children, style, enterDelay = 100, portalContainer, ...rest }, ref) { const arrowRef = useRef(null); const [open, setOpen] = useState(false); const { rootElement } = useEds(); const shouldOpen = Boolean(title) && typeof document !== 'undefined'; const { x, y, refs, strategy, context, middlewareData: { arrow: { x: arrowX, y: arrowY } = {} }, placement: finalPlacement, elements } = useFloating({ placement, open, onOpenChange: setOpen, middleware: [offset(14), flip(), shift({ padding: 8 }), arrow({ element: arrowRef })], whileElementsMounted: autoUpdate }); const anchorRef = useMemo(() => mergeRefs(refs.setReference, children?.ref), [refs.setReference, children?.ref]); const tooltipRef = useMemo(() => mergeRefs(refs.setFloating, ref), [refs.setFloating, ref]); const { getReferenceProps, getFloatingProps } = useInteractions([useHover(context, { delay: { open: enterDelay } }), useFocus(context), useRole(context, { role: 'tooltip' }), useDismiss(context)]); useEffect(() => { const staticSide = { top: 'bottom', right: 'left', bottom: 'top', left: 'right' }[finalPlacement.split('-')[0]]; let arrowTransform = 'none'; switch (staticSide) { case 'right': arrowTransform = 'rotateY(180deg)'; break; case 'left': arrowTransform = 'none'; break; case 'top': arrowTransform = 'rotate(90deg)'; break; case 'bottom': arrowTransform = 'rotate(-90deg)'; break; } if (arrowRef.current) { Object.assign(arrowRef.current.style, { left: arrowX != null ? `${arrowX}px` : '', top: arrowY != null ? `${arrowY}px` : '', right: '', bottom: '', [staticSide]: '-6px', transform: arrowTransform }); } }); const updatedChildren = /*#__PURE__*/cloneElement(children, { ...getReferenceProps({ ref: anchorRef, ...children.props }) }); useEffect(() => { if (!elements.floating) return; if (elements.floating.isConnected && shouldOpen && open) { elements.floating.showPopover(); } }, [open, shouldOpen, elements.floating]); const TooltipEl = /*#__PURE__*/jsxs(StyledTooltip, { popover: "manual", ...rest, ...getFloatingProps({ ref: tooltipRef, className: `eds-tooltip ${rest.className ?? ''}`, style: { ...style, position: strategy, top: y || 0, left: x || 0 } }), children: [title, /*#__PURE__*/jsx(ArrowWrapper, { ref: arrowRef, children: /*#__PURE__*/jsx(TooltipArrow, { className: "arrowSvg", children: /*#__PURE__*/jsx("path", { d: "M0.504838 4.86885C-0.168399 4.48524 -0.168399 3.51476 0.504838 3.13115L6 8.59227e-08L6 8L0.504838 4.86885Z" }) }) })] }); return /*#__PURE__*/jsxs(Fragment, { children: [shouldOpen && open && /*#__PURE__*/createPortal(TooltipEl, portalContainer ?? rootElement ?? document.body), updatedChildren] }); }); export { Tooltip };