import { useMemo, useState, useEffect } from '@wordpress/element'; import classnames from 'classnames'; import { usePopper } from 'react-popper'; import { LiveResizeConfiguration, liveResizeModifier } from '../utils/live-resize-modifier'; import Overlay from './tour-kit-overlay'; import { SpotlightInteractivity, SpotlightInteractivityConfiguration, } from './tour-kit-spotlight-interactivity'; import type { Rect, Placement } from '@popperjs/core'; export const SPOTLIT_ELEMENT_CLASS = 'wp-tour-kit-spotlit'; interface Props { referenceElement: HTMLElement | null; styles?: React.CSSProperties; interactivity?: SpotlightInteractivityConfiguration; liveResize?: LiveResizeConfiguration; } const TourKitSpotlight: React.FunctionComponent< Props > = ( { referenceElement, styles, interactivity, liveResize, } ) => { const [ popperElement, sePopperElement ] = useState< HTMLElement | null >( null ); const referenceRect = referenceElement?.getBoundingClientRect(); const modifiers = [ { name: 'flip', enabled: false, }, { name: 'preventOverflow', options: { mainAxis: false, // true by default }, }, useMemo( () => ( { name: 'offset', options: { offset: ( { placement, reference, popper, }: { placement: Placement; reference: Rect; popper: Rect; } ): [ number, number ] => { if ( placement === 'bottom' ) { return [ 0, -( reference.height + ( popper.height - reference.height ) / 2 ) ]; } return [ 0, 0 ]; }, }, } ), [] ), // useMemo because https://popper.js.org/react-popper/v2/faq/#why-i-get-render-loop-whenever-i-put-a-function-inside-the-popper-configuration useMemo( () => { return liveResizeModifier( liveResize ); }, [ liveResize ] ), ]; const { styles: popperStyles, attributes: popperAttributes } = usePopper( referenceElement, popperElement, { strategy: 'fixed', placement: 'bottom', modifiers, } ); const clipDimensions = referenceRect ? { width: `${ referenceRect.width }px`, height: `${ referenceRect.height }px`, } : null; const clipRepositionProps = referenceElement ? { style: { ...( clipDimensions && clipDimensions ), ...popperStyles?.popper, ...( styles && styles ), }, ...popperAttributes?.popper, } : null; /** * Add a .wp-spotlit class to the referenced element so that we can * apply CSS styles to it, for whatever purposes such as interactivity */ useEffect( () => { referenceElement?.classList.add( SPOTLIT_ELEMENT_CLASS ); return () => { referenceElement?.classList.remove( SPOTLIT_ELEMENT_CLASS ); }; }, [ referenceElement ] ); return ( <>
) } /> ); }; export default TourKitSpotlight;