github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/web/src/Tooltip.tsx (about) 1 import { makeStyles } from "@material-ui/core/styles" 2 import Tooltip, { TooltipProps } from "@material-ui/core/Tooltip" 3 import React from "react" 4 import { useStorageState } from "react-storage-hooks" 5 import styled from "styled-components" 6 import { ReactComponent as InfoSvg } from "./assets/svg/info.svg" 7 import { InstrumentedButton } from "./instrumentedComponents" 8 import { 9 Color, 10 ColorRGBA, 11 Font, 12 FontSize, 13 mixinResetButtonStyle, 14 SizeUnit, 15 } from "./style-helpers" 16 17 const INFO_TOOLTIP_LEAVE_DELAY = 500 18 19 let useStyles = makeStyles((theme: any) => ({ 20 arrow: { 21 color: Color.grayLightest, 22 "&::before": { 23 border: `1px solid ${Color.gray50}`, 24 }, 25 }, 26 tooltip: { 27 backgroundColor: Color.grayLightest, 28 fontFamily: Font.sansSerif, 29 fontSize: FontSize.smallest, 30 fontWeight: 400, 31 color: Color.gray20, 32 padding: SizeUnit(0.25), 33 border: `1px solid ${Color.gray50}`, 34 }, 35 popper: { 36 filter: "drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.25))", 37 }, 38 })) 39 40 export default function TiltTooltip(props: TooltipProps) { 41 const classes = useStyles() 42 43 return ( 44 <Tooltip 45 arrow 46 placement="top-start" 47 classes={classes} 48 role="tooltip" 49 enterNextDelay={500} 50 {...props} 51 /> 52 ) 53 } 54 55 const InfoIcon = styled(InfoSvg)` 56 cursor: pointer; 57 margin: ${SizeUnit(0.25)}; 58 flex-shrink: 0; 59 60 &.shadow { 61 border-radius: 50%; 62 box-shadow: 0px 0px 5px 2px ${ColorRGBA(Color.gray20, 0.6)}; 63 } 64 65 .fillStd { 66 fill: ${Color.gray60}; 67 } 68 ` 69 const DismissButton = styled(InstrumentedButton)` 70 ${mixinResetButtonStyle}; 71 72 width: 100%; 73 74 .MuiButton-label { 75 font-size: ${FontSize.smallester}; 76 font-style: italic; 77 color: ${Color.gray50}; 78 text-align: right; 79 display: block; 80 } 81 ` 82 83 export interface InfoTooltipProps { 84 dismissId?: string // If set, this tooltip will be dismissable, keyed by this string 85 displayShadow?: boolean 86 idForIcon?: string // Use to semantically associate the tooltip with another element through `aria-describedby` or `aria-labelledby` 87 } 88 89 export function TiltInfoTooltip( 90 props: Omit<TooltipProps, "children"> & InfoTooltipProps 91 ) { 92 const { displayShadow, idForIcon, title, dismissId, ...tooltipProps } = props 93 const shadowClass = displayShadow ? "shadow" : "" 94 95 // bug: if multiple tooltips are on the same page with the same key, 96 // "dismiss" will only affect the tooltip clicked, until next refresh 97 // https://app.clubhouse.io/windmill/story/12654/modifying-usepersistentstate-state-doesn-t-update-other-components-in-the-same-page-using-the-same-key 98 const [dismissed, setDismissed] = useStorageState( 99 localStorage, 100 `tooltip-dismissed-${props.dismissId}`, 101 false 102 ) 103 if (dismissed) { 104 return null 105 } 106 107 let content = title 108 109 if (dismissId !== undefined) { 110 content = ( 111 <> 112 <div>{title}</div> 113 <DismissButton 114 size="small" 115 analyticsName={"ui.web.dismissTooltip"} 116 analyticsTags={{ tooltip: props.dismissId }} 117 onClick={() => setDismissed(true)} 118 > 119 Don't show this tip 120 </DismissButton> 121 </> 122 ) 123 } 124 125 return ( 126 <TiltTooltip 127 interactive 128 leaveDelay={INFO_TOOLTIP_LEAVE_DELAY} 129 title={content} 130 {...tooltipProps} 131 > 132 <InfoIcon id={idForIcon} height={16} width={16} className={shadowClass} /> 133 </TiltTooltip> 134 ) 135 }