/** * External dependencies */ import PropTypes from 'prop-types'; import { __ } from '@wordpress/i18n'; import { Popover, Card, CardBody, CardHeader, CardFooter, Button, IsolatedEventContainer, } from '@wordpress/components'; import { useState, useEffect, createPortal, useLayoutEffect, } from '@wordpress/element'; import { close } from '@wordpress/icons'; import { noop } from 'lodash'; /** * Internal dependencies */ import './style.scss'; const SHOW_CLASS = 'highlight-tooltip__show'; function HighlightTooltip( { title, closeButtonText, content, show = true, id, onClose, delay, onShow = noop, useAnchor = false, } ) { const [ showHighlight, setShowHighlight ] = useState( delay > 0 ? null : show ); const [ node, setNode ] = useState( null ); const [ anchorRect, setAnchorRect ] = useState( null ); useEffect( () => { const element = document.getElementById( id ); let container, parent; if ( element && ! node ) { // Add tooltip container if ( ! useAnchor ) { parent = element.parentElement; } else { parent = document.createElement( 'div' ); document.body.appendChild( parent ); } container = document.createElement( 'div' ); container.classList.add( 'highlight-tooltip__container' ); parent.appendChild( container ); setNode( container ); } const timeoutId = triggerShowTooltip( container ); return () => { if ( container ) { const parentElement = container.parentElement; parentElement.removeChild( container ); if ( useAnchor ) { parentElement.remove(); } } if ( timeoutId ) { clearTimeout( timeoutId ); } }; }, [] ); useEffect( () => { if ( ! showHighlight && node ) { node.classList.remove( SHOW_CLASS ); } }, [ showHighlight ] ); useEffect( () => { if ( show !== showHighlight && showHighlight !== null && node ) { setShowHighlight( show ); if ( ! show ) { node.classList.remove( SHOW_CLASS ); } else if ( node ) { triggerShowTooltip( node ); } } }, [ show ] ); useLayoutEffect( () => { window.addEventListener( 'resize', updateSize ); return () => window.removeEventListener( 'resize', updateSize ); }, [] ); function updateSize() { if ( useAnchor ) { const element = document.getElementById( id ); setAnchorRect( element.getBoundingClientRect() ); } } const triggerShowTooltip = ( container ) => { let timeoutId = null; if ( delay > 0 ) { timeoutId = setTimeout( () => { timeoutId = null; showTooltip( container ); }, delay ); } else if ( ! showHighlight ) { showTooltip( container ); } return timeoutId; }; const showTooltip = ( container ) => { const element = document.getElementById( id ); if ( element && useAnchor ) { setAnchorRect( element.getBoundingClientRect() ); } if ( container ) { container.classList.add( SHOW_CLASS ); } setShowHighlight( true ); onShow(); }; const triggerClose = () => { setShowHighlight( false ); if ( onClose ) { onClose(); } }; if ( ! node ) { return null; } return createPortal(
{ showHighlight ? ( <> { title } ) : null }
, node ); } HighlightTooltip.propTypes = { /** * The id of the element it should highlight, should be unique per HighlightTooltip. */ id: PropTypes.string.isRequired, /** * Title of the popup */ title: PropTypes.string.isRequired, /** * Text of the close button. */ closeButtonText: PropTypes.string.isRequired, /** * Content of the popup, can be either text or react element. */ content: PropTypes.oneOfType( [ PropTypes.string, PropTypes.node ] ), /** * If to show the popup, defaults to true. */ show: PropTypes.bool, /** * Callback for when the user closes the popup. */ onClose: PropTypes.func, /** * This will delay the popup from appearing by the number of ms. */ delay: PropTypes.number, /** * A callback for when the tooltip is shown. */ onShow: PropTypes.func, /** * useAnchor, will append the tooltip to the body tag, and make use of the anchorRect to display the tooltip. * Defaults to false. */ useAnchor: PropTypes.bool, }; export { HighlightTooltip };