UNPKG

@dr.pogodin/react-utils

Version:

Collection of generic ReactJS components and utils

25 lines 3.5 kB
/* global window */import{useEffect,useRef,useState}from"react";import themed from"@dr.pogodin/react-themes";import Tooltip,{PLACEMENTS}from"./Tooltip.js";const defaultTheme={"ad":"n6OiU9","hoc":"zwsjGc","context":"JVJPcU","appearance":"_9U4YiR","arrow":"_9n65k-","container":"uA1tHZ","wrapper":"JFVmGf"};import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";/** * Implements a simple to use and themeable tooltip component, _e.g._ * ```js * <WithTooltip tip="This is example tooltip."> * <p>Hover to see the tooltip.</p> * </WithTooltip> * ``` * **Children:** Children are rendered in the place of `<WithTooltip>`, * and when hovered the tooltip is shown. By default the wrapper itself is * `<div>` block with `display: inline-block`. * @param tip &ndash; Anything React is able to render, * _e.g._ a tooltip text. This will be the tooltip content. * @param {WithTooltipTheme} props.theme _Ad hoc_ theme. */const Wrapper=({children,placement=PLACEMENTS.ABOVE_CURSOR,tip,theme})=>{const{current:heap}=useRef({lastCursorX:0,lastCursorY:0,timerId:undefined,triggeredByTouch:false});const tooltipRef=useRef(null);const wrapperRef=useRef(null);const[showTooltip,setShowTooltip]=useState(false);const updatePortalPosition=(cursorX,cursorY)=>{if(showTooltip){const wrapperRect=wrapperRef.current.getBoundingClientRect();if(cursorX<wrapperRect.left||cursorX>wrapperRect.right||cursorY<wrapperRect.top||cursorY>wrapperRect.bottom){setShowTooltip(false)}else if(tooltipRef.current){tooltipRef.current.pointTo(cursorX+window.scrollX,cursorY+window.scrollY,placement,wrapperRef.current)}}else{heap.lastCursorX=cursorX;heap.lastCursorY=cursorY;// If tooltip was triggered by a touch, we delay its opening by a bit, // to ensure it was not a touch-click - in the case of touch click we // want to do the click, rather than show the tooltip, and the delay // gives click handler a chance to abort the tooltip openning. if(heap.triggeredByTouch){heap.timerId??=setTimeout(()=>{heap.triggeredByTouch=false;heap.timerId=undefined;setShowTooltip(true)},300);// Otherwise we can just open the tooltip right away. }else setShowTooltip(true)}};useEffect(()=>{if(showTooltip&&tip!==null){// This is necessary to ensure that even when a single mouse event // arrives to a tool-tipped component, the tooltip is correctly positioned // once opened (because similar call above does not have effect until // the tooltip is fully mounted, and that is delayed to future rendering // cycle due to the implementation). if(tooltipRef.current){tooltipRef.current.pointTo(heap.lastCursorX+window.scrollX,heap.lastCursorY+window.scrollY,placement,wrapperRef.current)}const listener=()=>{setShowTooltip(false)};window.addEventListener("scroll",listener);return()=>{window.removeEventListener("scroll",listener)}}return undefined},[heap.lastCursorX,heap.lastCursorY,placement,showTooltip,tip]);return/*#__PURE__*/_jsxs("div",{className:theme.wrapper,onClick:()=>{if(heap.timerId){clearTimeout(heap.timerId);heap.timerId=undefined;heap.triggeredByTouch=false}},onMouseLeave:()=>{setShowTooltip(false)},onMouseMove:e=>{updatePortalPosition(e.clientX,e.clientY)},onTouchStart:()=>{heap.triggeredByTouch=true},ref:wrapperRef,role:"presentation",children:[showTooltip&&tip!==null?/*#__PURE__*/_jsx(Tooltip,{ref:tooltipRef,theme:theme,children:tip}):null,children]})};const ThemedWrapper=themed(Wrapper,"WithTooltip",defaultTheme);const e=ThemedWrapper;e.PLACEMENTS=PLACEMENTS;export default e; //# sourceMappingURL=index.js.map