import { PanelMenuItem } from '@grafana/data'; import { AngularComponent, getDataSourceSrv, locationService } from '@grafana/runtime'; import { PanelCtrl } from 'app/angular/panel/panel_ctrl'; import config from 'app/core/config'; import { DashboardModel } from 'app/features/dashboard/state/DashboardModel'; import { PanelModel } from 'app/features/dashboard/state/PanelModel'; import { addLibraryPanel, copyPanel, duplicatePanel, removePanel, sharePanel, unlinkLibraryPanel, } from 'app/features/dashboard/utils/panel'; import { isPanelModelLibraryPanel } from 'app/features/library-panels/guard'; import { store } from 'app/store/store'; import { contextSrv } from '../../../core/services/context_srv'; import { getExploreUrl } from '../../../core/utils/explore'; import { navigateToExplore } from '../../explore/state/main'; import { getTimeSrv } from '../services/TimeSrv'; export function getPanelMenu( dashboard: DashboardModel, panel: PanelModel, angularComponent?: AngularComponent | null ): PanelMenuItem[] { const onViewPanel = (event: React.MouseEvent) => { event.preventDefault(); locationService.partial({ viewPanel: panel.id, }); }; const onEditPanel = (event: React.MouseEvent) => { event.preventDefault(); locationService.partial({ editPanel: panel.id, }); }; const onSharePanel = (event: React.MouseEvent) => { event.preventDefault(); sharePanel(dashboard, panel); }; const onAddLibraryPanel = (event: React.MouseEvent) => { event.preventDefault(); addLibraryPanel(dashboard, panel); }; const onUnlinkLibraryPanel = (event: React.MouseEvent) => { event.preventDefault(); unlinkLibraryPanel(panel); }; const onInspectPanel = (tab?: string) => { locationService.partial({ inspect: panel.id, inspectTab: tab, }); }; const onMore = (event: React.MouseEvent) => { event.preventDefault(); }; const onDuplicatePanel = (event: React.MouseEvent) => { event.preventDefault(); duplicatePanel(dashboard, panel); }; const onCopyPanel = (event: React.MouseEvent) => { event.preventDefault(); copyPanel(panel); }; const onRemovePanel = (event: React.MouseEvent) => { event.preventDefault(); removePanel(dashboard, panel, true); }; const onNavigateToExplore = (event: React.MouseEvent) => { event.preventDefault(); const openInNewWindow = event.ctrlKey || event.metaKey ? (url: string) => window.open(`${config.appSubUrl}${url}`) : undefined; store.dispatch(navigateToExplore(panel, { getDataSourceSrv, getTimeSrv, getExploreUrl, openInNewWindow }) as any); }; const menu: PanelMenuItem[] = []; if (!panel.isEditing) { menu.push({ text: 'View', iconClassName: 'eye', onClick: onViewPanel, shortcut: 'v', }); } if (dashboard.canEditPanel(panel) && !panel.isEditing) { menu.push({ text: 'Edit', iconClassName: 'edit', onClick: onEditPanel, shortcut: 'e', }); } menu.push({ text: 'Share', iconClassName: 'share-alt', onClick: onSharePanel, shortcut: 'p s', }); if (contextSrv.hasAccessToExplore() && !(panel.plugin && panel.plugin.meta.skipDataQuery)) { menu.push({ text: 'Explore', iconClassName: 'compass', shortcut: 'x', onClick: onNavigateToExplore, }); } const inspectMenu: PanelMenuItem[] = []; // Only show these inspect actions for data plugins if (panel.plugin && !panel.plugin.meta.skipDataQuery) { inspectMenu.push({ text: 'Data', onClick: (e: React.MouseEvent) => onInspectPanel('data'), }); if (dashboard.meta.canEdit) { inspectMenu.push({ text: 'Query', onClick: (e: React.MouseEvent) => onInspectPanel('query'), }); } } inspectMenu.push({ text: 'Panel JSON', onClick: (e: React.MouseEvent) => onInspectPanel('json'), }); menu.push({ type: 'submenu', text: 'Inspect', iconClassName: 'info-circle', onClick: (e: React.MouseEvent) => onInspectPanel(), shortcut: 'i', subMenu: inspectMenu, }); const subMenu: PanelMenuItem[] = []; if (dashboard.canEditPanel(panel) && !(panel.isViewing || panel.isEditing)) { subMenu.push({ text: 'Duplicate', onClick: onDuplicatePanel, shortcut: 'p d', }); subMenu.push({ text: 'Copy', onClick: onCopyPanel, }); if (isPanelModelLibraryPanel(panel)) { subMenu.push({ text: 'Unlink library panel', onClick: onUnlinkLibraryPanel, }); } else { subMenu.push({ text: 'Create library panel', onClick: onAddLibraryPanel, }); } } // add old angular panel options if (angularComponent) { const scope = angularComponent.getScope(); const panelCtrl: PanelCtrl = scope.$$childHead.ctrl; const angularMenuItems = panelCtrl.getExtendedMenu(); for (const item of angularMenuItems) { const reactItem: PanelMenuItem = { text: item.text, href: item.href, shortcut: item.shortcut, }; if (item.click) { reactItem.onClick = () => { scope.$eval(item.click, { ctrl: panelCtrl }); }; } subMenu.push(reactItem); } } if (!panel.isEditing && subMenu.length) { menu.push({ type: 'submenu', text: 'More...', iconClassName: 'cube', subMenu, onClick: onMore, }); } if (dashboard.canEditPanel(panel) && !panel.isEditing && !panel.isViewing) { menu.push({ type: 'divider', text: '' }); menu.push({ text: 'Remove', iconClassName: 'trash-alt', onClick: onRemovePanel, shortcut: 'p r', }); } return menu; }