renderer.ts 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. ///<reference path="../../../headers/common.d.ts" />
  2. import _ from 'lodash';
  3. import moment from 'moment';
  4. import kbn from 'app/core/utils/kbn';
  5. export class TableRenderer {
  6. formatters: any[];
  7. colorState: any;
  8. constructor(private panel, private table, private isUtc, private sanitize, private templateSrv) {
  9. this.initColumns();
  10. }
  11. setTable(table) {
  12. this.table = table;
  13. this.initColumns();
  14. }
  15. initColumns() {
  16. this.formatters = [];
  17. this.colorState = {};
  18. for (let colIndex = 0; colIndex < this.table.columns.length; colIndex++) {
  19. let column = this.table.columns[colIndex];
  20. column.title = column.text;
  21. for (let i = 0; i < this.panel.styles.length; i++) {
  22. let style = this.panel.styles[i];
  23. var regex = kbn.stringToJsRegex(style.pattern);
  24. if (column.text.match(regex)) {
  25. column.style = style;
  26. if (style.alias) {
  27. column.title = column.text.replace(regex, style.alias);
  28. }
  29. break;
  30. }
  31. }
  32. this.formatters[colIndex] = this.createColumnFormatter(column);
  33. }
  34. }
  35. getColorForValue(value, style) {
  36. if (!style.thresholds) { return null; }
  37. for (var i = style.thresholds.length; i > 0; i--) {
  38. if (value >= style.thresholds[i - 1]) {
  39. return style.colors[i];
  40. }
  41. }
  42. return _.first(style.colors);
  43. }
  44. defaultCellFormatter(v, style) {
  45. if (v === null || v === void 0 || v === undefined) {
  46. return '';
  47. }
  48. if (_.isArray(v)) {
  49. v = v.join(', ');
  50. }
  51. if (style && style.sanitize) {
  52. return this.sanitize(v);
  53. } else {
  54. return _.escape(v);
  55. }
  56. }
  57. createColumnFormatter(column) {
  58. if (!column.style) {
  59. return this.defaultCellFormatter;
  60. }
  61. if (column.style.type === 'hidden') {
  62. return v => {
  63. return undefined;
  64. };
  65. }
  66. if (column.style.type === 'date') {
  67. return v => {
  68. if (v === undefined || v === null) {
  69. return '-';
  70. }
  71. if (_.isArray(v)) { v = v[0]; }
  72. var date = moment(v);
  73. if (this.isUtc) {
  74. date = date.utc();
  75. }
  76. return date.format(column.style.dateFormat);
  77. };
  78. }
  79. if (column.style.type === 'number') {
  80. let valueFormatter = kbn.valueFormats[column.unit || column.style.unit];
  81. return v => {
  82. if (v === null || v === void 0) {
  83. return '-';
  84. }
  85. if (_.isString(v)) {
  86. return this.defaultCellFormatter(v, column.style);
  87. }
  88. if (column.style.colorMode) {
  89. this.colorState[column.style.colorMode] = this.getColorForValue(v, column.style);
  90. }
  91. return valueFormatter(v, column.style.decimals, null);
  92. };
  93. }
  94. return (value) => {
  95. return this.defaultCellFormatter(value, column.style);
  96. };
  97. }
  98. renderRowVariables(rowIndex) {
  99. let scopedVars = {};
  100. let cell_variable;
  101. let row = this.table.rows[rowIndex];
  102. for (let i = 0; i < row.length; i++) {
  103. cell_variable = `__cell_${i}`;
  104. scopedVars[cell_variable] = { value: row[i] };
  105. }
  106. return scopedVars;
  107. }
  108. formatColumnValue(colIndex, value) {
  109. return this.formatters[colIndex] ? this.formatters[colIndex](value) : value;
  110. }
  111. renderCell(columnIndex, rowIndex, value, addWidthHack = false) {
  112. value = this.formatColumnValue(columnIndex, value);
  113. var style = '';
  114. var cellClasses = [];
  115. var cellClass = '';
  116. if (this.colorState.cell) {
  117. style = ' style="background-color:' + this.colorState.cell + ';color: white"';
  118. this.colorState.cell = null;
  119. } else if (this.colorState.value) {
  120. style = ' style="color:' + this.colorState.value + '"';
  121. this.colorState.value = null;
  122. }
  123. // because of the fixed table headers css only solution
  124. // there is an issue if header cell is wider the cell
  125. // this hack adds header content to cell (not visible)
  126. var widthHack = '';
  127. if (addWidthHack) {
  128. widthHack = '<div class="table-panel-width-hack">' + this.table.columns[columnIndex].title + '</div>';
  129. }
  130. if (value === undefined) {
  131. style = ' style="display:none;"';
  132. this.table.columns[columnIndex].hidden = true;
  133. } else {
  134. this.table.columns[columnIndex].hidden = false;
  135. }
  136. var columnStyle = this.table.columns[columnIndex].style;
  137. if (columnStyle && columnStyle.preserveFormat) {
  138. cellClasses.push("table-panel-cell-pre");
  139. }
  140. var columnHtml = value + widthHack;
  141. if (columnStyle && columnStyle.link) {
  142. // Render cell as link
  143. var scopedVars = this.renderRowVariables(rowIndex);
  144. scopedVars['__cell'] = { value: value };
  145. var cellLink = this.templateSrv.replace(columnStyle.linkUrl, scopedVars);
  146. var cellLinkTooltip = this.templateSrv.replace(columnStyle.linkTooltip, scopedVars);
  147. var cellTarget = columnStyle.linkTargetBlank ? '_blank' : '';
  148. cellClasses.push("table-panel-cell-link");
  149. columnHtml = `
  150. <a href="${cellLink}" target="${cellTarget}" data-link-tooltip data-original-title="${cellLinkTooltip}" data-placement="right">
  151. ${columnHtml}
  152. </a>
  153. `;
  154. }
  155. if (cellClasses.length) {
  156. cellClass = ' class="' + cellClasses.join(' ') + '"';
  157. }
  158. columnHtml = '<td' + cellClass + style + '>' + columnHtml + '</td>';
  159. return columnHtml;
  160. }
  161. render(page) {
  162. let pageSize = this.panel.pageSize || 100;
  163. let startPos = page * pageSize;
  164. let endPos = Math.min(startPos + pageSize, this.table.rows.length);
  165. var html = "";
  166. for (var y = startPos; y < endPos; y++) {
  167. let row = this.table.rows[y];
  168. let cellHtml = '';
  169. let rowStyle = '';
  170. for (var i = 0; i < this.table.columns.length; i++) {
  171. cellHtml += this.renderCell(i, y, row[i], y === startPos);
  172. }
  173. if (this.colorState.row) {
  174. rowStyle = ' style="background-color:' + this.colorState.row + ';color: white"';
  175. this.colorState.row = null;
  176. }
  177. html += '<tr ' + rowStyle + '>' + cellHtml + '</tr>';
  178. }
  179. return html;
  180. }
  181. render_values() {
  182. let rows = [];
  183. for (var y = 0; y < this.table.rows.length; y++) {
  184. let row = this.table.rows[y];
  185. let new_row = [];
  186. for (var i = 0; i < this.table.columns.length; i++) {
  187. new_row.push(this.formatColumnValue(i, row[i]));
  188. }
  189. rows.push(new_row);
  190. }
  191. return {
  192. columns: this.table.columns,
  193. rows: rows,
  194. };
  195. }
  196. }