panel_model.ts 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. import { Emitter } from 'app/core/utils/emitter';
  2. import _ from 'lodash';
  3. import { PANEL_OPTIONS_KEY_PREFIX } from 'app/core/constants';
  4. export interface GridPos {
  5. x: number;
  6. y: number;
  7. w: number;
  8. h: number;
  9. static?: boolean;
  10. }
  11. const notPersistedProperties: { [str: string]: boolean } = {
  12. events: true,
  13. fullscreen: true,
  14. isEditing: true,
  15. hasRefreshed: true,
  16. cachedPluginOptions: true,
  17. };
  18. // For angular panels we need to clean up properties when changing type
  19. // To make sure the change happens without strange bugs happening when panels use same
  20. // named property with different type / value expectations
  21. // This is not required for react panels
  22. const mustKeepProps: { [str: string]: boolean } = {
  23. id: true,
  24. gridPos: true,
  25. type: true,
  26. title: true,
  27. scopedVars: true,
  28. repeat: true,
  29. repeatIteration: true,
  30. repeatPanelId: true,
  31. repeatDirection: true,
  32. repeatedByRow: true,
  33. minSpan: true,
  34. collapsed: true,
  35. panels: true,
  36. targets: true,
  37. datasource: true,
  38. timeFrom: true,
  39. timeShift: true,
  40. hideTimeOverride: true,
  41. maxDataPoints: true,
  42. interval: true,
  43. description: true,
  44. links: true,
  45. fullscreen: true,
  46. isEditing: true,
  47. hasRefreshed: true,
  48. events: true,
  49. cacheTimeout: true,
  50. nullPointMode: true,
  51. cachedPluginOptions: true,
  52. transparent: true,
  53. };
  54. const defaults: any = {
  55. gridPos: { x: 0, y: 0, h: 3, w: 6 },
  56. datasource: null,
  57. targets: [{}],
  58. cachedPluginOptions: {},
  59. transparent: false,
  60. };
  61. export class PanelModel {
  62. id: number;
  63. gridPos: GridPos;
  64. type: string;
  65. title: string;
  66. alert?: any;
  67. scopedVars?: any;
  68. repeat?: string;
  69. repeatIteration?: number;
  70. repeatPanelId?: number;
  71. repeatDirection?: string;
  72. repeatedByRow?: boolean;
  73. minSpan?: number;
  74. collapsed?: boolean;
  75. panels?: any;
  76. soloMode?: boolean;
  77. targets: any[];
  78. datasource: string;
  79. thresholds?: any;
  80. snapshotData?: any;
  81. timeFrom?: any;
  82. timeShift?: any;
  83. hideTimeOverride?: any;
  84. maxDataPoints?: number;
  85. interval?: string;
  86. description?: string;
  87. links?: [];
  88. transparent: boolean;
  89. // non persisted
  90. fullscreen: boolean;
  91. isEditing: boolean;
  92. hasRefreshed: boolean;
  93. events: Emitter;
  94. cacheTimeout?: any;
  95. // cache props between plugins
  96. cachedPluginOptions?: any;
  97. constructor(model) {
  98. this.events = new Emitter();
  99. // copy properties from persisted model
  100. for (const property in model) {
  101. this[property] = model[property];
  102. }
  103. // defaults
  104. _.defaultsDeep(this, _.cloneDeep(defaults));
  105. }
  106. getOptions(panelDefaults) {
  107. return _.defaultsDeep(this[this.getOptionsKey()] || {}, panelDefaults);
  108. }
  109. updateOptions(options: object) {
  110. const update: any = {};
  111. update[this.getOptionsKey()] = options;
  112. Object.assign(this, update);
  113. this.render();
  114. }
  115. private getOptionsKey() {
  116. return PANEL_OPTIONS_KEY_PREFIX + this.type;
  117. }
  118. getSaveModel() {
  119. const model: any = {};
  120. for (const property in this) {
  121. if (notPersistedProperties[property] || !this.hasOwnProperty(property)) {
  122. continue;
  123. }
  124. if (_.isEqual(this[property], defaults[property])) {
  125. continue;
  126. }
  127. model[property] = _.cloneDeep(this[property]);
  128. }
  129. return model;
  130. }
  131. setViewMode(fullscreen: boolean, isEditing: boolean) {
  132. this.fullscreen = fullscreen;
  133. this.isEditing = isEditing;
  134. this.events.emit('view-mode-changed');
  135. }
  136. updateGridPos(newPos: GridPos) {
  137. let sizeChanged = false;
  138. if (this.gridPos.w !== newPos.w || this.gridPos.h !== newPos.h) {
  139. sizeChanged = true;
  140. }
  141. this.gridPos.x = newPos.x;
  142. this.gridPos.y = newPos.y;
  143. this.gridPos.w = newPos.w;
  144. this.gridPos.h = newPos.h;
  145. if (sizeChanged) {
  146. this.events.emit('panel-size-changed');
  147. }
  148. }
  149. resizeDone() {
  150. this.events.emit('panel-size-changed');
  151. }
  152. refresh() {
  153. this.hasRefreshed = true;
  154. this.events.emit('refresh');
  155. }
  156. render() {
  157. if (!this.hasRefreshed) {
  158. this.refresh();
  159. } else {
  160. this.events.emit('render');
  161. }
  162. }
  163. initialized() {
  164. this.events.emit('panel-initialized');
  165. }
  166. private getOptionsToRemember() {
  167. return Object.keys(this).reduce((acc, property) => {
  168. if (notPersistedProperties[property] || mustKeepProps[property]) {
  169. return acc;
  170. }
  171. return {
  172. ...acc,
  173. [property]: this[property],
  174. };
  175. }, {});
  176. }
  177. private saveCurrentPanelOptions() {
  178. this.cachedPluginOptions[this.type] = this.getOptionsToRemember();
  179. }
  180. private restorePanelOptions(pluginId: string) {
  181. const prevOptions = this.cachedPluginOptions[pluginId] || {};
  182. Object.keys(prevOptions).map(property => {
  183. this[property] = prevOptions[property];
  184. });
  185. }
  186. changeType(pluginId: string, fromAngularPanel: boolean) {
  187. this.saveCurrentPanelOptions();
  188. this.type = pluginId;
  189. // for angular panels only we need to remove all events and let angular panels do some cleanup
  190. if (fromAngularPanel) {
  191. this.destroy();
  192. for (const key of _.keys(this)) {
  193. if (mustKeepProps[key]) {
  194. continue;
  195. }
  196. delete this[key];
  197. }
  198. }
  199. this.restorePanelOptions(pluginId);
  200. }
  201. destroy() {
  202. this.events.emit('panel-teardown');
  203. this.events.removeAllListeners();
  204. }
  205. }