github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/webapp/javascript/components/TimelineChart/ContextMenu.plugin.tsx (about) 1 import React from 'react'; 2 import * as ReactDOM from 'react-dom'; 3 import { randomId } from '@webapp/util/randomId'; 4 5 // Pre calculated once 6 // TODO(eh-am): does this work with multiple contextMenus? 7 const WRAPPER_ID = randomId('context_menu'); 8 9 export interface ContextMenuProps { 10 click: { 11 /** The X position in the window where the click originated */ 12 pageX: number; 13 /** The Y position in the window where the click originated */ 14 pageY: number; 15 }; 16 timestamp: number; 17 containerEl: HTMLElement; 18 } 19 20 (function ($: JQueryStatic) { 21 function init(plot: jquery.flot.plot & jquery.flot.plotOptions) { 22 const placeholder = plot.getPlaceholder(); 23 24 function onClick( 25 event: unknown, 26 pos: { x: number; pageX: number; pageY: number } 27 ) { 28 const options: jquery.flot.plotOptions & { 29 ContextMenu?: React.FC<ContextMenuProps>; 30 } = plot.getOptions(); 31 const container = inject($); 32 const containerEl = container?.[0]; 33 34 // unmount any previous menus 35 ReactDOM.unmountComponentAtNode(containerEl); 36 37 const ContextMenu = options?.ContextMenu; 38 39 if (ContextMenu && containerEl) { 40 ReactDOM.render( 41 <ContextMenu 42 click={{ ...pos }} 43 containerEl={containerEl} 44 timestamp={Math.round(pos.x / 1000)} 45 />, 46 containerEl 47 ); 48 } 49 } 50 51 // Register events and shutdown 52 // It's important to bind/unbind to the SAME element 53 // Since a plugin may be register/unregistered multiple times due to react re-rendering 54 plot.hooks!.bindEvents!.push(function () { 55 placeholder.bind('plotclick', onClick); 56 }); 57 58 plot.hooks!.shutdown!.push(function () { 59 placeholder.unbind('plotclick', onClick); 60 61 const container = inject($); 62 63 ReactDOM.unmountComponentAtNode(container?.[0]); 64 }); 65 } 66 67 $.plot.plugins.push({ 68 init, 69 options: {}, 70 name: 'context_menu', 71 version: '1.0', 72 }); 73 })(jQuery); 74 75 function inject($: JQueryStatic) { 76 const alreadyInitialized = $(`#${WRAPPER_ID}`).length > 0; 77 78 if (alreadyInitialized) { 79 return $(`#${WRAPPER_ID}`); 80 } 81 82 const body = $('body'); 83 return $(`<div id="${WRAPPER_ID}" />`).appendTo(body); 84 }