2019-01-17 10:59:47 -06:00
|
|
|
// Libraries
|
2017-12-20 05:33:33 -06:00
|
|
|
import _ from 'lodash';
|
2019-01-17 10:59:47 -06:00
|
|
|
|
|
|
|
// Types
|
|
|
|
import { Emitter } from 'app/core/utils/emitter';
|
2018-11-16 03:00:13 -06:00
|
|
|
import { PANEL_OPTIONS_KEY_PREFIX } from 'app/core/constants';
|
2019-01-30 06:43:17 -06:00
|
|
|
import { DataQuery, TimeSeries } from '@grafana/ui';
|
2017-10-10 02:34:14 -05:00
|
|
|
|
2017-10-10 07:20:53 -05:00
|
|
|
export interface GridPos {
|
2017-10-10 02:34:14 -05:00
|
|
|
x: number;
|
|
|
|
y: number;
|
2017-10-10 07:20:53 -05:00
|
|
|
w: number;
|
|
|
|
h: number;
|
2017-10-16 02:55:55 -05:00
|
|
|
static?: boolean;
|
2017-10-10 07:20:53 -05:00
|
|
|
}
|
|
|
|
|
2017-12-19 09:06:54 -06:00
|
|
|
const notPersistedProperties: { [str: string]: boolean } = {
|
|
|
|
events: true,
|
|
|
|
fullscreen: true,
|
2017-12-20 05:33:33 -06:00
|
|
|
isEditing: true,
|
2018-08-25 10:49:39 -05:00
|
|
|
hasRefreshed: true,
|
2018-12-04 07:19:55 -06:00
|
|
|
cachedPluginOptions: true,
|
2017-10-10 07:20:53 -05:00
|
|
|
};
|
|
|
|
|
2018-11-16 03:00:13 -06:00
|
|
|
// For angular panels we need to clean up properties when changing type
|
|
|
|
// To make sure the change happens without strange bugs happening when panels use same
|
|
|
|
// named property with different type / value expectations
|
|
|
|
// This is not required for react panels
|
|
|
|
|
|
|
|
const mustKeepProps: { [str: string]: boolean } = {
|
|
|
|
id: true,
|
|
|
|
gridPos: true,
|
|
|
|
type: true,
|
|
|
|
title: true,
|
|
|
|
scopedVars: true,
|
|
|
|
repeat: true,
|
|
|
|
repeatIteration: true,
|
|
|
|
repeatPanelId: true,
|
|
|
|
repeatDirection: true,
|
|
|
|
repeatedByRow: true,
|
|
|
|
minSpan: true,
|
|
|
|
collapsed: true,
|
|
|
|
panels: true,
|
|
|
|
targets: true,
|
|
|
|
datasource: true,
|
|
|
|
timeFrom: true,
|
|
|
|
timeShift: true,
|
|
|
|
hideTimeOverride: true,
|
|
|
|
maxDataPoints: true,
|
|
|
|
interval: true,
|
|
|
|
description: true,
|
|
|
|
links: true,
|
|
|
|
fullscreen: true,
|
|
|
|
isEditing: true,
|
|
|
|
hasRefreshed: true,
|
|
|
|
events: true,
|
|
|
|
cacheTimeout: true,
|
2018-12-04 07:19:55 -06:00
|
|
|
cachedPluginOptions: true,
|
2018-12-06 03:34:27 -06:00
|
|
|
transparent: true,
|
2018-11-16 03:00:13 -06:00
|
|
|
};
|
|
|
|
|
2018-10-14 14:14:11 -05:00
|
|
|
const defaults: any = {
|
|
|
|
gridPos: { x: 0, y: 0, h: 3, w: 6 },
|
|
|
|
datasource: null,
|
2019-01-21 13:35:24 -06:00
|
|
|
targets: [{ refId: 'A' }],
|
2018-12-04 07:19:55 -06:00
|
|
|
cachedPluginOptions: {},
|
2018-12-06 03:34:27 -06:00
|
|
|
transparent: false,
|
2018-10-14 14:14:11 -05:00
|
|
|
};
|
|
|
|
|
2017-10-10 07:20:53 -05:00
|
|
|
export class PanelModel {
|
|
|
|
id: number;
|
2017-12-19 09:06:54 -06:00
|
|
|
gridPos: GridPos;
|
2017-10-10 02:34:14 -05:00
|
|
|
type: string;
|
|
|
|
title: string;
|
2017-10-11 09:08:56 -05:00
|
|
|
alert?: any;
|
2017-10-12 12:01:02 -05:00
|
|
|
scopedVars?: any;
|
2017-10-13 07:50:16 -05:00
|
|
|
repeat?: string;
|
|
|
|
repeatIteration?: number;
|
|
|
|
repeatPanelId?: number;
|
|
|
|
repeatDirection?: string;
|
2018-01-10 07:30:43 -06:00
|
|
|
repeatedByRow?: boolean;
|
2018-08-20 08:33:49 -05:00
|
|
|
maxPerRow?: number;
|
2017-10-17 07:53:52 -05:00
|
|
|
collapsed?: boolean;
|
2017-10-17 05:04:18 -05:00
|
|
|
panels?: any;
|
2017-11-28 02:38:28 -06:00
|
|
|
soloMode?: boolean;
|
2019-01-21 13:35:24 -06:00
|
|
|
targets: DataQuery[];
|
2018-06-26 09:32:01 -05:00
|
|
|
datasource: string;
|
2018-08-25 14:22:50 -05:00
|
|
|
thresholds?: any;
|
2017-10-11 04:42:49 -05:00
|
|
|
|
2019-01-30 06:43:17 -06:00
|
|
|
snapshotData?: TimeSeries[];
|
2018-11-08 07:44:12 -06:00
|
|
|
timeFrom?: any;
|
|
|
|
timeShift?: any;
|
|
|
|
hideTimeOverride?: any;
|
|
|
|
|
2018-11-12 03:35:46 -06:00
|
|
|
maxDataPoints?: number;
|
|
|
|
interval?: string;
|
2018-11-14 01:07:16 -06:00
|
|
|
description?: string;
|
2018-11-15 09:52:39 -06:00
|
|
|
links?: [];
|
2018-12-06 03:34:27 -06:00
|
|
|
transparent: boolean;
|
2018-11-12 02:32:55 -06:00
|
|
|
|
2017-10-11 04:42:49 -05:00
|
|
|
// non persisted
|
|
|
|
fullscreen: boolean;
|
|
|
|
isEditing: boolean;
|
2018-08-25 10:49:39 -05:00
|
|
|
hasRefreshed: boolean;
|
2017-10-10 07:20:53 -05:00
|
|
|
events: Emitter;
|
2018-11-20 09:33:26 -06:00
|
|
|
cacheTimeout?: any;
|
2017-10-10 07:20:53 -05:00
|
|
|
|
2018-12-04 07:19:55 -06:00
|
|
|
// cache props between plugins
|
|
|
|
cachedPluginOptions?: any;
|
|
|
|
|
2017-10-10 10:57:53 -05:00
|
|
|
constructor(model) {
|
|
|
|
this.events = new Emitter();
|
|
|
|
|
2017-10-10 07:20:53 -05:00
|
|
|
// copy properties from persisted model
|
2018-08-29 07:26:50 -05:00
|
|
|
for (const property in model) {
|
2017-10-10 07:20:53 -05:00
|
|
|
this[property] = model[property];
|
|
|
|
}
|
2017-10-12 14:37:27 -05:00
|
|
|
|
2018-06-26 09:32:01 -05:00
|
|
|
// defaults
|
2018-10-14 14:14:11 -05:00
|
|
|
_.defaultsDeep(this, _.cloneDeep(defaults));
|
2019-01-21 13:35:24 -06:00
|
|
|
// queries must have refId
|
|
|
|
this.ensureQueryIds();
|
|
|
|
}
|
|
|
|
|
|
|
|
ensureQueryIds() {
|
|
|
|
if (this.targets) {
|
|
|
|
for (const query of this.targets) {
|
|
|
|
if (!query.refId) {
|
|
|
|
query.refId = this.getNextQueryLetter();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-10-10 07:20:53 -05:00
|
|
|
}
|
|
|
|
|
2018-11-20 10:01:58 -06:00
|
|
|
getOptions(panelDefaults) {
|
|
|
|
return _.defaultsDeep(this[this.getOptionsKey()] || {}, panelDefaults);
|
2018-11-05 10:46:09 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
updateOptions(options: object) {
|
|
|
|
const update: any = {};
|
|
|
|
update[this.getOptionsKey()] = options;
|
|
|
|
Object.assign(this, update);
|
|
|
|
this.render();
|
|
|
|
}
|
|
|
|
|
|
|
|
private getOptionsKey() {
|
2018-12-05 00:33:21 -06:00
|
|
|
return PANEL_OPTIONS_KEY_PREFIX + this.type;
|
2018-11-05 10:46:09 -06:00
|
|
|
}
|
|
|
|
|
2017-10-10 07:20:53 -05:00
|
|
|
getSaveModel() {
|
2017-10-10 10:57:53 -05:00
|
|
|
const model: any = {};
|
2018-08-29 07:26:50 -05:00
|
|
|
for (const property in this) {
|
2017-10-10 07:20:53 -05:00
|
|
|
if (notPersistedProperties[property] || !this.hasOwnProperty(property)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-10-14 14:14:11 -05:00
|
|
|
if (_.isEqual(this[property], defaults[property])) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-10-12 14:37:27 -05:00
|
|
|
model[property] = _.cloneDeep(this[property]);
|
2017-10-10 07:20:53 -05:00
|
|
|
}
|
2017-10-10 10:57:53 -05:00
|
|
|
|
|
|
|
return model;
|
2017-10-10 07:20:53 -05:00
|
|
|
}
|
|
|
|
|
2017-10-11 04:42:49 -05:00
|
|
|
setViewMode(fullscreen: boolean, isEditing: boolean) {
|
|
|
|
this.fullscreen = fullscreen;
|
|
|
|
this.isEditing = isEditing;
|
2018-11-19 05:26:15 -06:00
|
|
|
this.events.emit('view-mode-changed');
|
2017-10-11 04:42:49 -05:00
|
|
|
}
|
|
|
|
|
2017-10-10 07:20:53 -05:00
|
|
|
updateGridPos(newPos: GridPos) {
|
|
|
|
let sizeChanged = false;
|
|
|
|
|
|
|
|
if (this.gridPos.w !== newPos.w || this.gridPos.h !== newPos.h) {
|
|
|
|
sizeChanged = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.gridPos.x = newPos.x;
|
|
|
|
this.gridPos.y = newPos.y;
|
|
|
|
this.gridPos.w = newPos.w;
|
|
|
|
this.gridPos.h = newPos.h;
|
|
|
|
|
|
|
|
if (sizeChanged) {
|
2017-12-20 05:33:33 -06:00
|
|
|
this.events.emit('panel-size-changed');
|
2017-10-10 07:20:53 -05:00
|
|
|
}
|
|
|
|
}
|
2017-10-11 14:36:03 -05:00
|
|
|
|
|
|
|
resizeDone() {
|
2017-12-20 05:33:33 -06:00
|
|
|
this.events.emit('panel-size-changed');
|
2017-10-11 14:36:03 -05:00
|
|
|
}
|
2017-10-16 09:09:23 -05:00
|
|
|
|
2018-08-25 10:49:39 -05:00
|
|
|
refresh() {
|
|
|
|
this.hasRefreshed = true;
|
|
|
|
this.events.emit('refresh');
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
if (!this.hasRefreshed) {
|
|
|
|
this.refresh();
|
|
|
|
} else {
|
|
|
|
this.events.emit('render');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-05 02:23:47 -06:00
|
|
|
initialized() {
|
2018-08-25 10:49:39 -05:00
|
|
|
this.events.emit('panel-initialized');
|
|
|
|
}
|
|
|
|
|
2018-12-05 00:33:21 -06:00
|
|
|
private getOptionsToRemember() {
|
2018-12-04 07:19:55 -06:00
|
|
|
return Object.keys(this).reduce((acc, property) => {
|
2018-12-05 00:33:21 -06:00
|
|
|
if (notPersistedProperties[property] || mustKeepProps[property]) {
|
2018-12-04 07:19:55 -06:00
|
|
|
return acc;
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
...acc,
|
|
|
|
[property]: this[property],
|
|
|
|
};
|
|
|
|
}, {});
|
|
|
|
}
|
|
|
|
|
2018-12-05 00:33:21 -06:00
|
|
|
private saveCurrentPanelOptions() {
|
|
|
|
this.cachedPluginOptions[this.type] = this.getOptionsToRemember();
|
2018-12-04 07:19:55 -06:00
|
|
|
}
|
|
|
|
|
2018-12-05 00:33:21 -06:00
|
|
|
private restorePanelOptions(pluginId: string) {
|
2018-12-04 07:39:56 -06:00
|
|
|
const prevOptions = this.cachedPluginOptions[pluginId] || {};
|
2018-12-04 07:19:55 -06:00
|
|
|
|
2018-12-05 00:33:21 -06:00
|
|
|
Object.keys(prevOptions).map(property => {
|
|
|
|
this[property] = prevOptions[property];
|
2018-12-04 07:19:55 -06:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-11-16 03:00:13 -06:00
|
|
|
changeType(pluginId: string, fromAngularPanel: boolean) {
|
2018-12-04 07:19:55 -06:00
|
|
|
this.saveCurrentPanelOptions();
|
2018-08-25 14:22:50 -05:00
|
|
|
this.type = pluginId;
|
|
|
|
|
2018-11-16 03:00:13 -06:00
|
|
|
// for angular panels only we need to remove all events and let angular panels do some cleanup
|
|
|
|
if (fromAngularPanel) {
|
|
|
|
this.destroy();
|
|
|
|
|
|
|
|
for (const key of _.keys(this)) {
|
2018-12-05 00:33:21 -06:00
|
|
|
if (mustKeepProps[key]) {
|
2018-11-16 03:00:13 -06:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
delete this[key];
|
|
|
|
}
|
|
|
|
}
|
2018-12-04 07:19:55 -06:00
|
|
|
|
|
|
|
this.restorePanelOptions(pluginId);
|
2018-07-09 11:17:51 -05:00
|
|
|
}
|
|
|
|
|
2018-12-12 01:54:12 -06:00
|
|
|
addQuery(query?: Partial<DataQuery>) {
|
2018-12-11 06:36:44 -06:00
|
|
|
query = query || { refId: 'A' };
|
2018-12-12 01:54:12 -06:00
|
|
|
query.refId = this.getNextQueryLetter();
|
2019-01-21 13:35:24 -06:00
|
|
|
this.targets.push(query as DataQuery);
|
2018-12-11 06:36:44 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
getNextQueryLetter(): string {
|
|
|
|
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
|
|
|
|
|
|
return _.find(letters, refId => {
|
|
|
|
return _.every(this.targets, other => {
|
|
|
|
return other.refId !== refId;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-10-16 09:09:23 -05:00
|
|
|
destroy() {
|
2018-10-31 05:25:00 -05:00
|
|
|
this.events.emit('panel-teardown');
|
2017-10-16 09:09:23 -05:00
|
|
|
this.events.removeAllListeners();
|
|
|
|
}
|
2017-10-10 02:34:14 -05:00
|
|
|
}
|