@dr.pogodin/react-utils
Version:
Collection of generic ReactJS components and utils
25 lines • 3.5 kB
JavaScript
/* 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 – 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