| 1 |
import React, { useState, useEffect, useRef } from 'react'; |
| 2 |
import { arrayToClassName } from '../utils/utils'; |
| 3 |
|
| 4 |
export default function Tooltip( props ) { |
| 5 |
const baseClassName = 'e-app-tooltip', |
| 6 |
classes = [ baseClassName, props.className ], |
| 7 |
childRef = useRef( null ), |
| 8 |
isAborted = useRef( false ), |
| 9 |
isManualControl = Object.prototype.hasOwnProperty.call( props, 'show' ), |
| 10 |
[ isLibraryLoaded, setIsLibraryLoaded ] = useState( false ), |
| 11 |
[ showTooltip, setShowTooltip ] = useState( false ), |
| 12 |
directionsMap = { |
| 13 |
top: 's', |
| 14 |
right: 'w', |
| 15 |
down: 'n', |
| 16 |
left: 'e', |
| 17 |
}, |
| 18 |
tipsyConfig = { |
| 19 |
trigger: isManualControl ? 'manual' : 'hover', |
| 20 |
gravity: directionsMap[ props.direction ], |
| 21 |
offset: props.offset, |
| 22 |
title() { |
| 23 |
return props.title; |
| 24 |
}, |
| 25 |
}, |
| 26 |
setTipsy = () => { |
| 27 |
const $tooltipContainer = jQuery( childRef.current ); |
| 28 |
|
| 29 |
$tooltipContainer.tipsy( tipsyConfig ); |
| 30 |
|
| 31 |
if ( isManualControl ) { |
| 32 |
const displayMode = showTooltip ? 'show' : 'hide'; |
| 33 |
|
| 34 |
$tooltipContainer.tipsy( displayMode ); |
| 35 |
} |
| 36 |
}; |
| 37 |
|
| 38 |
useEffect( () => { |
| 39 |
// In case that the component is disabled the tipsy library will not be loaded by default. |
| 40 |
if ( ! props.disabled ) { |
| 41 |
isAborted.current = false; |
| 42 |
|
| 43 |
import( |
| 44 |
/* webpackIgnore: true */ |
| 45 |
`${ elementorCommon.config.urls.assets }lib/tipsy/tipsy.min.js?ver=1.0.0` |
| 46 |
).then( () => { |
| 47 |
if ( ! isAborted.current ) { |
| 48 |
if ( isLibraryLoaded ) { |
| 49 |
setTipsy(); |
| 50 |
} else { |
| 51 |
setIsLibraryLoaded( true ); |
| 52 |
} |
| 53 |
} |
| 54 |
} ); |
| 55 |
} |
| 56 |
|
| 57 |
return () => { |
| 58 |
if ( ! props.disabled ) { |
| 59 |
// Aborting the current dynamic-import state update in case of re-render. |
| 60 |
isAborted.current = true; |
| 61 |
|
| 62 |
// Cleanup of existing tipsy element in case of re-render. |
| 63 |
const nodes = document.querySelectorAll( '.tipsy' ); |
| 64 |
nodes[ nodes.length - 1 ].remove(); |
| 65 |
} |
| 66 |
}; |
| 67 |
}, [ props.disabled ] ); |
| 68 |
|
| 69 |
useEffect( () => { |
| 70 |
if ( isLibraryLoaded ) { |
| 71 |
setTipsy(); |
| 72 |
} |
| 73 |
}, [ isLibraryLoaded, showTooltip ] ); |
| 74 |
|
| 75 |
useEffect( () => { |
| 76 |
// The "show" state should not be changed while the component is disabled. |
| 77 |
if ( ! props.disabled && props.show !== showTooltip ) { |
| 78 |
setShowTooltip( props.show ); |
| 79 |
} |
| 80 |
}, [ props.show ] ); |
| 81 |
|
| 82 |
return ( |
| 83 |
<props.tag className={ arrayToClassName( classes ) } ref={ childRef }> |
| 84 |
{ props.children } |
| 85 |
</props.tag> |
| 86 |
); |
| 87 |
} |
| 88 |
|
| 89 |
Tooltip.propTypes = { |
| 90 |
className: PropTypes.string, |
| 91 |
offset: PropTypes.number, |
| 92 |
show: PropTypes.bool, |
| 93 |
direction: PropTypes.oneOf( [ 'top', 'right', 'left', 'down' ] ), |
| 94 |
tag: PropTypes.string.isRequired, |
| 95 |
title: PropTypes.string.isRequired, |
| 96 |
disabled: PropTypes.bool, |
| 97 |
children: PropTypes.any, |
| 98 |
}; |
| 99 |
|
| 100 |
Tooltip.defaultProps = { |
| 101 |
className: '', |
| 102 |
offset: 10, |
| 103 |
direction: 'top', |
| 104 |
disabled: false, |
| 105 |
}; |
| 106 |
|