github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/packages/pyroscope-flamegraph/src/FlameGraph/FlameGraphComponent/DiffLegendPaletteDropdown.tsx (about) 1 import React from 'react'; 2 import cx from 'classnames'; 3 import useResizeObserver from '@react-hook/resize-observer'; 4 import { 5 ColorBlindPalette, 6 DefaultPalette, 7 FlamegraphPalette, 8 } from './colorPalette'; 9 import DiffLegend from './DiffLegend'; 10 import CheckIcon from './CheckIcon'; 11 // Until we migrate ui to its own package this should do it 12 // eslint-disable-next-line 13 import Dropdown, { MenuItem, MenuButton } from '@webapp/ui/Dropdown'; 14 // eslint-disable-next-line 15 import dropdownStyles from '@webapp/ui/Dropdown.module.scss'; 16 17 import styles from './DiffLegendPaletteDropdown.module.css'; 18 19 const paletteList = [DefaultPalette, ColorBlindPalette]; 20 21 interface DiffLegendPaletteDropdownProps { 22 palette: FlamegraphPalette; 23 onChange: (p: FlamegraphPalette) => void; 24 } 25 26 export const DiffLegendPaletteDropdown = ( 27 props: DiffLegendPaletteDropdownProps 28 ) => { 29 const { palette = DefaultPalette, onChange } = props; 30 const legendRef = React.useRef<HTMLDivElement>(null); 31 const showMode = useSizeMode(legendRef); 32 33 return ( 34 <> 35 <div className={styles.row} role="heading" aria-level={2}> 36 <p style={{ color: palette.goodColor.rgb().string() }}>(-) Removed</p> 37 <p style={{ color: palette.badColor.rgb().string() }}>Added (+)</p> 38 </div> 39 40 <div ref={legendRef} className={styles.dropdownWrapper}> 41 <Dropdown 42 label="Select a palette" 43 align="end" 44 menuButton={ 45 <MenuButton 46 className={cx( 47 // eslint-disable-next-line 48 dropdownStyles.dropdownMenuButton, 49 styles.diffPaletteDropdown 50 )} 51 > 52 <DiffLegend palette={palette} showMode={showMode} /> 53 </MenuButton> 54 } 55 onItemClick={(e) => onChange(e.value)} 56 > 57 {paletteList.map((p) => ( 58 <MenuItem key={p.name} value={p}> 59 <div> 60 <label>{p.name}</label> 61 <div className={styles.dropdownItem}> 62 <DiffLegend palette={p} showMode={showMode} /> 63 64 {p === palette ? <CheckIcon /> : null} 65 </div> 66 </div> 67 </MenuItem> 68 ))} 69 </Dropdown> 70 </div> 71 </> 72 ); 73 }; 74 75 /** 76 * TODO: unify this and toolbar's 77 * Custom hook that returns the size ('large' | 'small') 78 * that should be displayed 79 * based on the toolbar width 80 */ 81 // arbitrary value 82 // as a simple heuristic, try to run the comparison view 83 // and see when the buttons start to overlap 84 const WIDTH_THRESHOLD = 13 * 37; 85 const useSizeMode = (target: React.RefObject<HTMLDivElement>) => { 86 const [size, setSize] = React.useState<'large' | 'small'>('large'); 87 88 const calcMode = (width: number) => { 89 if (width < WIDTH_THRESHOLD) { 90 return 'small'; 91 } 92 return 'large'; 93 }; 94 95 React.useLayoutEffect(() => { 96 if (target.current) { 97 const { width } = target.current.getBoundingClientRect(); 98 99 setSize(calcMode(width)); 100 } 101 }, [target.current]); 102 103 useResizeObserver(target, (entry: ResizeObserverEntry) => { 104 setSize(calcMode(entry.contentRect.width)); 105 }); 106 107 return size; 108 }; 109 110 export default DiffLegendPaletteDropdown;