| 
									
										
										
										
											2023-01-03 11:55:46 -08:00
										 |  |  | // Copyright 2020 Signal Messenger, LLC
 | 
					
						
							| 
									
										
										
										
											2020-11-19 13:11:35 -05:00
										 |  |  | // SPDX-License-Identifier: AGPL-3.0-only
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import React from 'react'; | 
					
						
							| 
									
										
										
										
											2021-01-11 15:43:21 -06:00
										 |  |  | import classNames from 'classnames'; | 
					
						
							| 
									
										
										
										
											2021-10-25 12:06:13 -07:00
										 |  |  | import { noop } from 'lodash'; | 
					
						
							| 
									
										
										
										
											2020-11-19 13:11:35 -05:00
										 |  |  | import { Manager, Reference, Popper } from 'react-popper'; | 
					
						
							| 
									
										
										
										
											2021-10-18 17:10:22 -05:00
										 |  |  | import type { StrictModifiers } from '@popperjs/core'; | 
					
						
							| 
									
										
										
										
											2021-10-26 14:15:33 -05:00
										 |  |  | import type { Theme } from '../util/theme'; | 
					
						
							|  |  |  | import { themeClassName } from '../util/theme'; | 
					
						
							| 
									
										
										
										
											2021-10-25 12:06:13 -07:00
										 |  |  | import { refMerger } from '../util/refMerger'; | 
					
						
							| 
									
										
										
										
											2021-08-03 12:04:49 -05:00
										 |  |  | import { offsetDistanceModifier } from '../util/popperUtil'; | 
					
						
							| 
									
										
										
										
											2023-03-14 11:55:31 -04:00
										 |  |  | import { getInteractionMode } from '../services/InteractionMode'; | 
					
						
							| 
									
										
										
										
											2020-11-19 13:11:35 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-25 12:06:13 -07:00
										 |  |  | type EventWrapperPropsType = { | 
					
						
							|  |  |  |   children: React.ReactNode; | 
					
						
							|  |  |  |   onHoverChanged: (_: boolean) => void; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // React doesn't reliably fire `onMouseLeave` or `onMouseOut` events if wrapping a
 | 
					
						
							|  |  |  | //   disabled button. This uses native browser events to avoid that.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // See <https://lecstor.com/react-disabled-button-onmouseleave/>.
 | 
					
						
							|  |  |  | const TooltipEventWrapper = React.forwardRef< | 
					
						
							|  |  |  |   HTMLSpanElement, | 
					
						
							|  |  |  |   EventWrapperPropsType | 
					
						
							| 
									
										
										
										
											2022-11-17 16:45:19 -08:00
										 |  |  | >(function TooltipEvent({ onHoverChanged, children }, ref): JSX.Element { | 
					
						
							| 
									
										
										
										
											2021-10-25 12:06:13 -07:00
										 |  |  |   const wrapperRef = React.useRef<HTMLSpanElement | null>(null); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const on = React.useCallback(() => { | 
					
						
							|  |  |  |     onHoverChanged(true); | 
					
						
							|  |  |  |   }, [onHoverChanged]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const off = React.useCallback(() => { | 
					
						
							|  |  |  |     onHoverChanged(false); | 
					
						
							|  |  |  |   }, [onHoverChanged]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-12 20:34:02 +01:00
										 |  |  |   const onFocus = React.useCallback(() => { | 
					
						
							| 
									
										
										
										
											2023-03-14 11:55:31 -04:00
										 |  |  |     if (getInteractionMode() === 'keyboard') { | 
					
						
							| 
									
										
										
										
											2021-11-12 20:34:02 +01:00
										 |  |  |       on(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }, [on]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-25 12:06:13 -07:00
										 |  |  |   React.useEffect(() => { | 
					
						
							|  |  |  |     const wrapperEl = wrapperRef.current; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!wrapperEl) { | 
					
						
							|  |  |  |       return noop; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     wrapperEl.addEventListener('mouseenter', on); | 
					
						
							|  |  |  |     wrapperEl.addEventListener('mouseleave', off); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return () => { | 
					
						
							|  |  |  |       wrapperEl.removeEventListener('mouseenter', on); | 
					
						
							|  |  |  |       wrapperEl.removeEventListener('mouseleave', off); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   }, [on, off]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return ( | 
					
						
							|  |  |  |     <span | 
					
						
							| 
									
										
										
										
											2021-11-12 20:34:02 +01:00
										 |  |  |       onFocus={onFocus} | 
					
						
							| 
									
										
										
										
											2021-10-25 12:06:13 -07:00
										 |  |  |       onBlur={off} | 
					
						
							|  |  |  |       ref={refMerger<HTMLSpanElement>(ref, wrapperRef)} | 
					
						
							|  |  |  |     > | 
					
						
							|  |  |  |       {children} | 
					
						
							|  |  |  |     </span> | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | }); | 
					
						
							| 
									
										
										
										
											2020-12-07 14:43:19 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-19 13:11:35 -05:00
										 |  |  | export enum TooltipPlacement { | 
					
						
							|  |  |  |   Top = 'top', | 
					
						
							|  |  |  |   Right = 'right', | 
					
						
							|  |  |  |   Bottom = 'bottom', | 
					
						
							|  |  |  |   Left = 'left', | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export type PropsType = { | 
					
						
							|  |  |  |   content: string | JSX.Element; | 
					
						
							| 
									
										
										
										
											2021-10-12 18:59:08 -05:00
										 |  |  |   className?: string; | 
					
						
							| 
									
										
										
										
											2022-11-17 16:45:19 -08:00
										 |  |  |   children?: React.ReactNode; | 
					
						
							| 
									
										
										
										
											2020-11-19 13:11:35 -05:00
										 |  |  |   direction?: TooltipPlacement; | 
					
						
							| 
									
										
										
										
											2021-10-18 17:10:22 -05:00
										 |  |  |   popperModifiers?: Array<StrictModifiers>; | 
					
						
							| 
									
										
										
										
											2020-11-19 13:11:35 -05:00
										 |  |  |   sticky?: boolean; | 
					
						
							| 
									
										
										
										
											2020-12-04 17:03:01 -06:00
										 |  |  |   theme?: Theme; | 
					
						
							| 
									
										
										
										
											2020-11-19 13:11:35 -05:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-17 16:45:19 -08:00
										 |  |  | export function Tooltip({ | 
					
						
							| 
									
										
										
										
											2020-11-19 13:11:35 -05:00
										 |  |  |   children, | 
					
						
							| 
									
										
										
										
											2021-10-12 18:59:08 -05:00
										 |  |  |   className, | 
					
						
							| 
									
										
										
										
											2020-11-19 13:11:35 -05:00
										 |  |  |   content, | 
					
						
							|  |  |  |   direction, | 
					
						
							|  |  |  |   sticky, | 
					
						
							| 
									
										
										
										
											2020-12-04 17:03:01 -06:00
										 |  |  |   theme, | 
					
						
							| 
									
										
										
										
											2021-10-18 17:10:22 -05:00
										 |  |  |   popperModifiers = [], | 
					
						
							| 
									
										
										
										
											2022-11-17 16:45:19 -08:00
										 |  |  | }: PropsType): JSX.Element { | 
					
						
							| 
									
										
										
										
											2020-12-07 14:43:19 -06:00
										 |  |  |   const [isHovering, setIsHovering] = React.useState(false); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const showTooltip = isHovering || Boolean(sticky); | 
					
						
							| 
									
										
										
										
											2020-11-19 13:11:35 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-11 15:43:21 -06:00
										 |  |  |   const tooltipThemeClassName = theme | 
					
						
							|  |  |  |     ? `module-tooltip--${themeClassName(theme)}` | 
					
						
							|  |  |  |     : undefined; | 
					
						
							| 
									
										
										
										
											2020-11-19 18:38:59 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-19 13:11:35 -05:00
										 |  |  |   return ( | 
					
						
							|  |  |  |     <Manager> | 
					
						
							|  |  |  |       <Reference> | 
					
						
							|  |  |  |         {({ ref }) => ( | 
					
						
							| 
									
										
										
										
											2021-10-25 12:06:13 -07:00
										 |  |  |           <TooltipEventWrapper ref={ref} onHoverChanged={setIsHovering}> | 
					
						
							| 
									
										
										
										
											2020-11-19 13:11:35 -05:00
										 |  |  |             {children} | 
					
						
							| 
									
										
										
										
											2021-10-25 12:06:13 -07:00
										 |  |  |           </TooltipEventWrapper> | 
					
						
							| 
									
										
										
										
											2020-11-19 13:11:35 -05:00
										 |  |  |         )} | 
					
						
							|  |  |  |       </Reference> | 
					
						
							| 
									
										
										
										
											2021-10-18 17:10:22 -05:00
										 |  |  |       <Popper | 
					
						
							|  |  |  |         placement={direction} | 
					
						
							|  |  |  |         modifiers={[offsetDistanceModifier(12), ...popperModifiers]} | 
					
						
							|  |  |  |       > | 
					
						
							| 
									
										
										
										
											2020-11-19 13:11:35 -05:00
										 |  |  |         {({ arrowProps, placement, ref, style }) => | 
					
						
							|  |  |  |           showTooltip && ( | 
					
						
							| 
									
										
										
										
											2021-01-11 15:43:21 -06:00
										 |  |  |             <div | 
					
						
							| 
									
										
										
										
											2021-10-12 18:59:08 -05:00
										 |  |  |               className={classNames( | 
					
						
							|  |  |  |                 'module-tooltip', | 
					
						
							|  |  |  |                 tooltipThemeClassName, | 
					
						
							|  |  |  |                 className | 
					
						
							|  |  |  |               )} | 
					
						
							| 
									
										
										
										
											2021-01-11 15:43:21 -06:00
										 |  |  |               ref={ref} | 
					
						
							|  |  |  |               style={style} | 
					
						
							|  |  |  |               data-placement={placement} | 
					
						
							|  |  |  |             > | 
					
						
							|  |  |  |               {content} | 
					
						
							| 
									
										
										
										
											2020-11-19 13:11:35 -05:00
										 |  |  |               <div | 
					
						
							| 
									
										
										
										
											2021-01-11 15:43:21 -06:00
										 |  |  |                 className="module-tooltip-arrow" | 
					
						
							|  |  |  |                 ref={arrowProps.ref} | 
					
						
							|  |  |  |                 style={arrowProps.style} | 
					
						
							|  |  |  |               /> | 
					
						
							| 
									
										
										
										
											2020-11-19 13:11:35 -05:00
										 |  |  |             </div> | 
					
						
							|  |  |  |           ) | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       </Popper> | 
					
						
							|  |  |  |     </Manager> | 
					
						
							|  |  |  |   ); | 
					
						
							| 
									
										
										
										
											2022-11-17 16:45:19 -08:00
										 |  |  | } |