@equinor/eds-core-react
Version:
The React implementation of the Equinor Design System
150 lines (147 loc) • 5.02 kB
JavaScript
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 };