1+ import { useEffect , useRef , useState } from 'react' ;
12import { useTheme } from '@emotion/react' ;
23import styled from '@emotion/styled' ;
34
45import { Button } from '@sentry/scraps/button' ;
56import { Flex , Stack } from '@sentry/scraps/layout' ;
67import { Separator } from '@sentry/scraps/separator' ;
78import { Text } from '@sentry/scraps/text' ;
8- import { Tooltip } from '@sentry/scraps/tooltip' ;
99
1010import { IconChevron } from 'sentry/icons' ;
1111import { t } from 'sentry/locale' ;
@@ -168,9 +168,25 @@ function OverlayControls({
168168 showOverlay : boolean ;
169169} ) {
170170 const theme = useTheme ( ) ;
171+ const [ isColorPickerOpen , setIsColorPickerOpen ] = useState ( false ) ;
172+ const pickerRef = useRef < HTMLDivElement > ( null ) ;
171173
172174 const overlayColors = theme . chart . getColorPalette ( 10 ) ;
173175
176+ useEffect ( ( ) => {
177+ if ( ! isColorPickerOpen ) {
178+ return undefined ;
179+ }
180+
181+ function handleMouseDown ( e : MouseEvent ) {
182+ if ( pickerRef . current && ! pickerRef . current . contains ( e . target as Node ) ) {
183+ setIsColorPickerOpen ( false ) ;
184+ }
185+ }
186+ document . addEventListener ( 'mousedown' , handleMouseDown ) ;
187+ return ( ) => document . removeEventListener ( 'mousedown' , handleMouseDown ) ;
188+ } , [ isColorPickerOpen ] ) ;
189+
174190 return (
175191 < Flex align = "center" gap = "sm" >
176192 < Button
@@ -180,52 +196,75 @@ function OverlayControls({
180196 >
181197 { showOverlay ? t ( 'Hide Overlay' ) : t ( 'Show Overlay' ) }
182198 </ Button >
183- < Tooltip
184- isHoverable
185- maxWidth = { 400 }
186- title = {
187- < Flex gap = "xs" >
188- { overlayColors . map ( color => (
189- < ColorSwatch
190- key = { color }
191- $color = { color }
192- $selected = { overlayColor === color }
193- onClick = { ( ) => onOverlayColorChange ( color ) }
194- aria-label = { t ( 'Overlay color %s' , color ) }
195- />
196- ) ) }
197- </ Flex >
198- }
199- >
200- < ColorTrigger $color = { overlayColor } aria-label = { t ( 'Pick overlay color' ) } />
201- </ Tooltip >
199+ < ColorPickerWrapper ref = { pickerRef } >
200+ < ColorTrigger
201+ color = { overlayColor }
202+ aria-label = { t ( 'Pick overlay color' ) }
203+ onClick = { ( ) => setIsColorPickerOpen ( open => ! open ) }
204+ />
205+ { isColorPickerOpen && (
206+ < ColorPickerDropdown >
207+ < Flex gap = "xs" >
208+ { overlayColors . map ( color => (
209+ < ColorSwatch
210+ key = { color }
211+ color = { color }
212+ selected = { overlayColor === color }
213+ onClick = { ( ) => {
214+ onOverlayColorChange ( color ) ;
215+ setIsColorPickerOpen ( false ) ;
216+ } }
217+ aria-label = { t ( 'Overlay color %s' , color ) }
218+ />
219+ ) ) }
220+ </ Flex >
221+ </ ColorPickerDropdown >
222+ ) }
223+ </ ColorPickerWrapper >
202224 </ Flex >
203225 ) ;
204226}
205227
206- const ColorTrigger = styled ( 'button' ) < { $color : string } > `
228+ const ColorPickerWrapper = styled ( 'div' ) `
229+ position: relative;
230+ ` ;
231+
232+ const ColorPickerDropdown = styled ( 'div' ) `
233+ position: absolute;
234+ top: 100%;
235+ right: 0;
236+ margin-top: ${ p => p . theme . space . xs } ;
237+ padding: ${ p => p . theme . space . sm } ;
238+ background: ${ p => p . theme . tokens . background . primary } ;
239+ border: 1px solid ${ p => p . theme . tokens . border . primary } ;
240+ border-radius: ${ p => p . theme . radius . md } ;
241+ box-shadow: ${ p => p . theme . dropShadowHeavy } ;
242+ z-index: ${ p => p . theme . zIndex . dropdown } ;
243+ ` ;
244+
245+ const ColorTrigger = styled ( 'button' ) < { color : string } > `
207246 width: 24px;
208247 height: 24px;
209248 border-radius: 50%;
210249 cursor: pointer;
211250 border: 2px solid ${ p => p . theme . tokens . border . primary } ;
212- background-color: ${ p => p . $ color} ;
251+ background-color: ${ p => p . color } ;
213252 padding: 0;
214253
215254 &:hover {
216255 border-color: ${ p => p . theme . tokens . border . accent } ;
217256 }
218257` ;
219258
220- const ColorSwatch = styled ( 'button' ) < { $ color : string ; $ selected : boolean } > `
259+ const ColorSwatch = styled ( 'button' ) < { color : string ; selected : boolean } > `
221260 width: 20px;
222261 height: 20px;
223262 border-radius: 50%;
224263 cursor: pointer;
225264 border: 2px solid
226- ${ p => ( p . $ selected ? p . theme . tokens . border . accent : p . theme . tokens . border . primary ) } ;
227- background-color: ${ p => p . $ color} ;
265+ ${ p => ( p . selected ? p . theme . tokens . border . accent : p . theme . tokens . border . primary ) } ;
266+ background-color: ${ p => p . color } ;
228267 padding: 0;
229- outline: ${ p => ( p . $ selected ? `2px solid ${ p . theme . tokens . focus . default } ` : 'none' ) } ;
268+ outline: ${ p => ( p . selected ? `2px solid ${ p . theme . tokens . focus . default } ` : 'none' ) } ;
230269 outline-offset: 1px;
231270` ;
0 commit comments