panel.ts 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. import { isString as _isString } from 'lodash';
  2. import { TimeRange, AppEvents, rangeUtil, dateMath, PanelModel as IPanelModel } from '@grafana/data';
  3. import { getTemplateSrv } from '@grafana/runtime';
  4. import appEvents from 'app/core/app_events';
  5. import config from 'app/core/config';
  6. import { LS_PANEL_COPY_KEY, PANEL_BORDER } from 'app/core/constants';
  7. import store from 'app/core/store';
  8. import { ShareModal } from 'app/features/dashboard/components/ShareModal';
  9. import { DashboardModel } from 'app/features/dashboard/state/DashboardModel';
  10. import { PanelModel } from 'app/features/dashboard/state/PanelModel';
  11. import { AddLibraryPanelModal } from 'app/features/library-panels/components/AddLibraryPanelModal/AddLibraryPanelModal';
  12. import { UnlinkModal } from 'app/features/library-panels/components/UnlinkModal/UnlinkModal';
  13. import { cleanUpPanelState } from 'app/features/panel/state/actions';
  14. import { dispatch } from 'app/store/store';
  15. import { ShowConfirmModalEvent, ShowModalReactEvent } from '../../../types/events';
  16. export const removePanel = (dashboard: DashboardModel, panel: PanelModel, ask: boolean) => {
  17. // confirm deletion
  18. if (ask !== false) {
  19. const text2 =
  20. panel.alert && !config.unifiedAlertingEnabled
  21. ? 'Panel includes an alert rule. removing the panel will also remove the alert rule'
  22. : undefined;
  23. const confirmText = panel.alert ? 'YES' : undefined;
  24. appEvents.publish(
  25. new ShowConfirmModalEvent({
  26. title: 'Remove panel',
  27. text: 'Are you sure you want to remove this panel?',
  28. text2: text2,
  29. icon: 'trash-alt',
  30. confirmText: confirmText,
  31. yesText: 'Remove',
  32. onConfirm: () => removePanel(dashboard, panel, false),
  33. })
  34. );
  35. return;
  36. }
  37. dashboard.removePanel(panel);
  38. dispatch(cleanUpPanelState(panel.key));
  39. };
  40. export const duplicatePanel = (dashboard: DashboardModel, panel: PanelModel) => {
  41. dashboard.duplicatePanel(panel);
  42. };
  43. export const copyPanel = (panel: IPanelModel) => {
  44. let saveModel = panel;
  45. if (panel instanceof PanelModel) {
  46. saveModel = panel.getSaveModel();
  47. }
  48. store.set(LS_PANEL_COPY_KEY, JSON.stringify(saveModel));
  49. appEvents.emit(AppEvents.alertSuccess, ['Panel copied. Click **Add panel** icon to paste.']);
  50. };
  51. export const sharePanel = (dashboard: DashboardModel, panel: PanelModel) => {
  52. appEvents.publish(
  53. new ShowModalReactEvent({
  54. component: ShareModal,
  55. props: {
  56. dashboard: dashboard,
  57. panel: panel,
  58. },
  59. })
  60. );
  61. };
  62. export const addLibraryPanel = (dashboard: DashboardModel, panel: PanelModel) => {
  63. appEvents.publish(
  64. new ShowModalReactEvent({
  65. component: AddLibraryPanelModal,
  66. props: {
  67. panel,
  68. initialFolderId: dashboard.meta.folderId,
  69. isOpen: true,
  70. },
  71. })
  72. );
  73. };
  74. export const unlinkLibraryPanel = (panel: PanelModel) => {
  75. appEvents.publish(
  76. new ShowModalReactEvent({
  77. component: UnlinkModal,
  78. props: {
  79. onConfirm: () => {
  80. delete panel.libraryPanel;
  81. panel.render();
  82. },
  83. isOpen: true,
  84. },
  85. })
  86. );
  87. };
  88. export const refreshPanel = (panel: PanelModel) => {
  89. panel.refresh();
  90. };
  91. export const toggleLegend = (panel: PanelModel) => {
  92. console.warn('Toggle legend is not implemented yet');
  93. // We need to set panel.legend defaults first
  94. // panel.legend.show = !panel.legend.show;
  95. refreshPanel(panel);
  96. };
  97. export interface TimeOverrideResult {
  98. timeRange: TimeRange;
  99. timeInfo: string;
  100. }
  101. export function applyPanelTimeOverrides(panel: PanelModel, timeRange: TimeRange): TimeOverrideResult {
  102. const newTimeData = {
  103. timeInfo: '',
  104. timeRange: timeRange,
  105. };
  106. if (panel.timeFrom) {
  107. const timeFromInterpolated = getTemplateSrv().replace(panel.timeFrom, panel.scopedVars);
  108. const timeFromInfo = rangeUtil.describeTextRange(timeFromInterpolated);
  109. if (timeFromInfo.invalid) {
  110. newTimeData.timeInfo = 'invalid time override';
  111. return newTimeData;
  112. }
  113. if (_isString(timeRange.raw.from)) {
  114. const timeFromDate = dateMath.parse(timeFromInfo.from)!;
  115. newTimeData.timeInfo = timeFromInfo.display;
  116. newTimeData.timeRange = {
  117. from: timeFromDate,
  118. to: dateMath.parse(timeFromInfo.to)!,
  119. raw: {
  120. from: timeFromInfo.from,
  121. to: timeFromInfo.to,
  122. },
  123. };
  124. }
  125. }
  126. if (panel.timeShift) {
  127. const timeShiftInterpolated = getTemplateSrv().replace(panel.timeShift, panel.scopedVars);
  128. const timeShiftInfo = rangeUtil.describeTextRange(timeShiftInterpolated);
  129. if (timeShiftInfo.invalid) {
  130. newTimeData.timeInfo = 'invalid timeshift';
  131. return newTimeData;
  132. }
  133. const timeShift = '-' + timeShiftInterpolated;
  134. newTimeData.timeInfo += ' timeshift ' + timeShift;
  135. const from = dateMath.parseDateMath(timeShift, newTimeData.timeRange.from, false)!;
  136. const to = dateMath.parseDateMath(timeShift, newTimeData.timeRange.to, true)!;
  137. newTimeData.timeRange = {
  138. from,
  139. to,
  140. raw: {
  141. from,
  142. to,
  143. },
  144. };
  145. }
  146. if (panel.hideTimeOverride) {
  147. newTimeData.timeInfo = '';
  148. }
  149. return newTimeData;
  150. }
  151. export function getResolution(panel: PanelModel): number {
  152. const htmlEl = document.getElementsByTagName('html')[0];
  153. const width = htmlEl.getBoundingClientRect().width; // https://stackoverflow.com/a/21454625
  154. return panel.maxDataPoints ? panel.maxDataPoints : Math.ceil(width * (panel.gridPos.w / 24));
  155. }
  156. export function calculateInnerPanelHeight(panel: PanelModel, containerHeight: number): number {
  157. const chromePadding = panel.plugin && panel.plugin.noPadding ? 0 : config.theme.panelPadding * 2;
  158. const headerHeight = panel.hasTitle() ? config.theme.panelHeaderHeight : 0;
  159. return containerHeight - headerHeight - chromePadding - PANEL_BORDER;
  160. }