The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
grafana/public/app/plugins/panel/table/renderer.ts

256 lines
6.9 KiB

import _ from 'lodash';
import moment from 'moment';
import kbn from 'app/core/utils/kbn';
export class TableRenderer {
formatters: any[];
colorState: any;
constructor(private panel, private table, private isUtc, private sanitize, private templateSrv) {
this.initColumns();
}
setTable(table) {
this.table = table;
this.initColumns();
}
initColumns() {
this.formatters = [];
this.colorState = {};
for (let colIndex = 0; colIndex < this.table.columns.length; colIndex++) {
let column = this.table.columns[colIndex];
column.title = column.text;
for (let i = 0; i < this.panel.styles.length; i++) {
let style = this.panel.styles[i];
var regex = kbn.stringToJsRegex(style.pattern);
if (column.text.match(regex)) {
column.style = style;
if (style.alias) {
column.title = column.text.replace(regex, style.alias);
}
break;
}
}
this.formatters[colIndex] = this.createColumnFormatter(column);
}
}
getColorForValue(value, style) {
if (!style.thresholds) { return null; }
for (var i = style.thresholds.length; i > 0; i--) {
if (value >= style.thresholds[i - 1]) {
return style.colors[i];
}
}
return _.first(style.colors);
}
defaultCellFormatter(v, style) {
if (v === null || v === void 0 || v === undefined) {
return '';
}
if (_.isArray(v)) {
v = v.join(', ');
}
if (style && style.sanitize) {
return this.sanitize(v);
} else {
return _.escape(v);
}
}
createColumnFormatter(column) {
if (!column.style) {
return this.defaultCellFormatter;
}
if (column.style.type === 'hidden') {
return v => {
return undefined;
};
}
if (column.style.type === 'date') {
return v => {
if (v === undefined || v === null) {
return '-';
}
if (_.isArray(v)) { v = v[0]; }
var date = moment(v);
if (this.isUtc) {
date = date.utc();
}
return date.format(column.style.dateFormat);
};
}
if (column.style.type === 'number') {
let valueFormatter = kbn.valueFormats[column.unit || column.style.unit];
return v => {
if (v === null || v === void 0) {
return '-';
}
if (_.isString(v)) {
return this.defaultCellFormatter(v, column.style);
}
if (column.style.colorMode) {
this.colorState[column.style.colorMode] = this.getColorForValue(v, column.style);
}
return valueFormatter(v, column.style.decimals, null);
};
}
return (value) => {
return this.defaultCellFormatter(value, column.style);
};
}
renderRowVariables(rowIndex) {
let scopedVars = {};
let cell_variable;
let row = this.table.rows[rowIndex];
for (let i = 0; i < row.length; i++) {
cell_variable = `__cell_${i}`;
scopedVars[cell_variable] = { value: row[i] };
}
return scopedVars;
}
formatColumnValue(colIndex, value) {
return this.formatters[colIndex] ? this.formatters[colIndex](value) : value;
}
renderCell(columnIndex, rowIndex, value, addWidthHack = false) {
value = this.formatColumnValue(columnIndex, value);
var column = this.table.columns[columnIndex];
var style = '';
var cellClasses = [];
var cellClass = '';
if (this.colorState.cell) {
style = ' style="background-color:' + this.colorState.cell + ';color: white"';
this.colorState.cell = null;
} else if (this.colorState.value) {
style = ' style="color:' + this.colorState.value + '"';
this.colorState.value = null;
}
// because of the fixed table headers css only solution
// there is an issue if header cell is wider the cell
// this hack adds header content to cell (not visible)
var columnHtml = '';
if (addWidthHack) {
columnHtml = '<div class="table-panel-width-hack">' + this.table.columns[columnIndex].title + '</div>';
}
if (value === undefined) {
style = ' style="display:none;"';
column.hidden = true;
} else {
column.hidden = false;
}
if (column.style && column.style.preserveFormat) {
cellClasses.push("table-panel-cell-pre");
}
if (column.style && column.style.link) {
// Render cell as link
var scopedVars = this.renderRowVariables(rowIndex);
scopedVars['__cell'] = { value: value };
var cellLink = this.templateSrv.replace(column.style.linkUrl, scopedVars);
var cellLinkTooltip = this.templateSrv.replace(column.style.linkTooltip, scopedVars);
var cellTarget = column.style.linkTargetBlank ? '_blank' : '';
cellClasses.push("table-panel-cell-link");
columnHtml += `
<a href="${cellLink}" target="${cellTarget}" data-link-tooltip data-original-title="${cellLinkTooltip}" data-placement="right">
${value}
</a>
`;
} else {
columnHtml += value;
}
if (column.filterable) {
cellClasses.push("table-panel-cell-filterable");
columnHtml += `
<a class="table-panel-filter-link" data-link-tooltip data-original-title="Filter out value" data-placement="bottom"
data-row="${rowIndex}" data-column="${columnIndex}" data-operator="!=">
<i class="fa fa-search-minus"></i>
</a>
<a class="table-panel-filter-link" data-link-tooltip data-original-title="Filter for value" data-placement="bottom"
data-row="${rowIndex}" data-column="${columnIndex}" data-operator="=">
<i class="fa fa-search-plus"></i>
</a>`;
}
if (cellClasses.length) {
cellClass = ' class="' + cellClasses.join(' ') + '"';
}
columnHtml = '<td' + cellClass + style + '>' + columnHtml + '</td>';
return columnHtml;
}
render(page) {
let pageSize = this.panel.pageSize || 100;
let startPos = page * pageSize;
let endPos = Math.min(startPos + pageSize, this.table.rows.length);
var html = "";
for (var y = startPos; y < endPos; y++) {
let row = this.table.rows[y];
let cellHtml = '';
let rowStyle = '';
for (var i = 0; i < this.table.columns.length; i++) {
cellHtml += this.renderCell(i, y, row[i], y === startPos);
}
if (this.colorState.row) {
rowStyle = ' style="background-color:' + this.colorState.row + ';color: white"';
this.colorState.row = null;
}
html += '<tr ' + rowStyle + '>' + cellHtml + '</tr>';
}
return html;
}
render_values() {
let rows = [];
for (var y = 0; y < this.table.rows.length; y++) {
let row = this.table.rows[y];
let new_row = [];
for (var i = 0; i < this.table.columns.length; i++) {
new_row.push(this.formatColumnValue(i, row[i]));
}
rows.push(new_row);
}
return {
columns: this.table.columns,
rows: rows,
};
}
}